Skip to content
engage-ai

Guides

Products and catalogue import

Add products one at a time or import a CSV or Shopify export. How SKUs, prices and availability work.

Products are the facts the AI quotes: names, prices, stock, sizes. You can add them one at a time with POST /products, or load a whole catalogue from a CSV or Shopify export with POST /products/import. Both paths run the same code, so an imported product is identical to one added by hand.

Every product is also turned into a knowledge document and indexed, so it becomes answerable a few seconds after you save it. You do not need a second call.

Add or update a product

curl -X POST https://api.getengageai.app/products \
  -H "Authorization: Bearer $ENGAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "BOOT-TRL-9",
    "name": "Trailhead Mid GTX Boot",
    "description": "Waterproof mid-cut hiking boot with a Gore-Tex membrane.",
    "category": "Footwear",
    "brand": "Acme",
    "priceAmount": 13500,
    "priceCurrency": "GBP",
    "availability": "IN_STOCK",
    "stockQuantity": 30,
    "attributes": { "sizes": "UK 4-13", "weight": "640 g" },
    "tags": ["hiking", "waterproof"],
    "url": "https://acme.example/products/trailhead-mid"
  }'

Only name is required. The fields the AI reads most are description and attributes, so put the details customers ask about there.

FieldNotes
skuYour identifier, unique within your organization. Up to 128 characters.
nameRequired. Up to 512 characters.
descriptionFree text, up to 20,000 characters. This is what the AI reads and quotes.
priceAmount, priceCurrencyMinor units and an ISO 4217 code: 13500 + GBP is £135.00.
availabilityIN_STOCK (default), OUT_OF_STOCK, PREORDER or DISCONTINUED. Controls what the AI may say about getting hold of the product.
stockQuantityA whole number, 0 or more.
attributesAny key-value facts: sizes, materials, dimensions. All of them are shown to the AI.
tagsUp to 50, each up to 64 characters.

How a product is matched

POST /products creates or updates. Which one depends on what you send:

  • sku you already have → updates that product. Replaying your whole feed every night is safe, and unchanged products are not re-indexed.
  • id from an earlier response → updates that product. Use this to change a product's SKU.
  • Neither, or a new sku → creates a product.

No SKU? One is derived from the name, so Enamel Mug, large becomes enamel-mug-large. Sending the same name again updates instead of duplicating. The catch: the name becomes the identity. Two different products with the same name need real SKUs, and renaming a product without a SKU creates a new one.

Read and delete

# A page of products (see cursor pagination)
curl "https://api.getengageai.app/products?limit=50" -H "Authorization: Bearer $ENGAGE_API_KEY"

# One product
curl https://api.getengageai.app/products/$PRODUCT_ID -H "Authorization: Bearer $ENGAGE_API_KEY"

# Delete. The AI stops using it immediately; past conversations stay readable.
curl -X DELETE https://api.getengageai.app/products/$PRODUCT_ID -H "Authorization: Bearer $ENGAGE_API_KEY"

Import a CSV file

Send the file as multipart/form-data in a field named file. Always run a dry run first: it validates every row and writes nothing.

curl -X POST "https://api.getengageai.app/products/import?dryRun=true" \
  -H "Authorization: Bearer $ENGAGE_API_KEY" \
  -F "file=@catalogue.csv"
{
  "dryRun": true,
  "total": 4,
  "imported": 3,
  "failed": 1,
  "failures": [{ "line": 4, "sku": "BAD-01", "message": "name is required" }],
  "truncated": false,
  "ignoredColumns": ["Colour"]
}

If the report looks right, send the same request without dryRun.

  • line is the line in the file, counting the header, so line 4 is the third product.
  • failed > 0 is a partial success. Rows are independent. Fix the listed rows and upload the whole file again; it updates rather than duplicates.
  • ignoredColumns lists headers that matched nothing. A misspelled Prixe column shows up here.
  • failures is capped at 100 entries, with truncated: true when there were more. failed is always the true count.

An import only adds and updates. It never deletes products missing from the file, and rows already written stay written if a later row fails.

Limits

LimitValue
File size5 MB (413 above it)
Rows5,000 per request
FormatCSV only. Excel and Word files are rejected with a message telling the person to save as CSV.
TimeAbout 3.5 ms per row, so 5,000 rows take around 18 seconds. The request stays open until it finishes, so set your HTTP timeout accordingly.

Columns

Columns are matched by name, ignoring case, spaces and punctuation, and their order does not matter. Only a name column is required.

FieldAccepted headers
SKUsku, code, productCode, itemCode, itemNumber, variantSku
Name (required)name, title, productName, productTitle
Descriptiondescription, details, body, bodyHtml, longDescription
Categorycategory, productType, type, department
Brandbrand, vendor, manufacturer, make
Price, as writtenprice, unitPrice, sellingPrice, retailPrice, variantPrice
Price, minor unitspriceAmount, priceMinor, priceCents, pricePence
Currencycurrency, priceCurrency, currencyCode
Availabilityavailability, stockStatus, availabilityStatus, inStock
Stockstock, stockQuantity, quantity, qty, inventory, onHand
Tagstags, labels, keywords
URLurl, link, productUrl, productLink

A minimal file:

SKU,Product Name,Description,Price,Currency,Qty,Stock Status,Tags
TENT-RIDGE-2P,Ridgeline 2P Tent,"A two-person, three-season tent. 2.1 kg packed.",135.00,GBP,24,in stock,camping;tent
BOOT-TRAIL-42,Trailhead Mid GTX Boot,"Waterproof mid-height hiking boot. Sizes 38-47.",129.99,GBP,8,in stock,footwear;hiking

How values are read

  • Prices are read the way people write them: 13.50, £1,350.00, 1,350.00. Symbols and thousands separators are removed and the text is converted without floating point. The currency's own minor unit is used, so 1000 in JPY stays ¥1000.
  • A price without a currency column is an error. The import will not guess a currency.
  • Availability is matched loosely: in stock, available or yes → IN_STOCK; sold out, oos or no → OUT_OF_STOCK; pre-order, backorder or coming soon → PREORDER; discontinued, retired or archived → DISCONTINUED. Anything else fails that row. Blank means IN_STOCK.
  • Tags split on ;, | or ,. Use ;, since a comma also separates columns.
  • Quoted fields keep their commas, line breaks and doubled quotes. Excel's byte-order mark is removed.

Shopify exports

Upload the file from Shopify's Products → Export → CSV as it is. The format is detected from its headers and the report says "format": "shopify".

curl -X POST "https://api.getengageai.app/products/import?currency=GBP&dryRun=true" \
  -H "Authorization: Bearer $ENGAGE_API_KEY" \
  -F "file=@products_export.csv"
  • Pass currency. A Shopify export has no currency column. Without it, every priced row fails.
  • One product per Handle. Variant and image rows are merged, so total counts products, not lines.
  • Stock is summed across variants, and the price is the first variant's.
  • Body (HTML) becomes plain text, and archived products become DISCONTINUED.
  • The SKU is the first variant's SKU, or the Handle if there is none. Re-exporting and re-uploading updates the same products.

Errors

StatusExample message
413File too large
422No file was uploaded. Send it as multipart/form-data under the field name 'file'.
422This looks like a zipped Office file (.xlsx, .docx, .ods), and only CSV can be read. …
422No product name column found. Expected one of: … Found: <your headers>
422The file has 5001 rows, and 5000 is the most that can be imported in one request.

These messages are written for the person who uploaded the file, so you can show them as they are.