API reference
Products
Products — the facts the AI quotes from
For concepts and a walkthrough, read Products and catalogue import.
List products
/productsCursor-paginated, newest first.
Query parameters
cursorstringFrom a previous response. Opaque.
limitintegerDefault 25, max 100.
Max 100
Response fields 200
itemsProduct[]requirednextCursorstring | nullrequiredOpaque. Pass back verbatim as
cursor. Null means the end.hasMorebooleanrequired
Product
skustringYour identifier, unique within your organization. Sending one you already have updates that product rather than conflicting with it.
Omit it and one is derived from the name — "Enamel Mug, large" becomes
enamel-mug-large— so a business with no SKUs can still add products, and adding the same name twice updates rather than duplicates. Give products real SKUs once two of them share a name.Max 128 characters
namestringrequiredMax 512 characters
descriptionstringFree text. This is what the AI actually reads and quotes from.
Max 20000 characters
categorystringMax 255 characters
brandstringMax 255 characters
priceAmountintegerMinor units. 13500 is 135.00 in the currency below.
priceCurrencystringISO 4217, e.g. GBP
Min 3 characters · Max 3 characters
availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED· Default"IN_STOCK"stockQuantityintegerattributesobjectrequiredArbitrary facts (weight, materials, sizes). Rendered into the AI context.
tagsstring[]requiredUp to 50 items
urlstring (uri)idstring (uuid)requiredvariantsobject[]variants[].namestringrequiredvariants[].skustring | nullrequiredvariants[].optionsobjectrequiredvariants[].priceAmountinteger | nullrequiredvariants[].availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED
Responses
- 200A page of products.
- 401Missing, malformed, revoked or expired API key.
- 404Not found, or not visible to this organization.
- 422Request failed validation.
- 429Rate limited.
- 500Unexpected error. The correlationId ties it to our logs.
curl "https://api.getengageai.app/products" \
-H "Authorization: Bearer $ENGAGE_API_KEY"{
"items": [
{
"sku": "BOOT-TRL-9",
"name": "Trailhead Mid GTX Boot",
"description": "Waterproof mid-cut hiking boot with a Gore-Tex membrane. Sizes UK 4-13.",
"category": "Footwear",
"brand": "Acme",
"priceAmount": 13500,
"priceCurrency": "GBP",
"availability": "IN_STOCK",
"stockQuantity": 30,
"attributes": {
"sizes": "UK 4-13",
"weight": "640 g"
},
"tags": [
"string"
],
"url": "https://acme.example/products/trailhead-mid",
"id": "3f6c2a1e-8b4d-4e7a-9c15-2d0b7e6f4a91",
"variants": [
{
"name": "Trailhead Mid GTX Boot",
"sku": "BOOT-TRL-9",
"options": {},
"priceAmount": 13500,
"availability": "IN_STOCK"
}
]
}
],
"nextCursor": null,
"hasMore": false
}Create or update a product
/productsWhich one it is depends on what identifies the product:
sku— your identifier, unique within your organization. Sending a SKU you already have updates that product, which is what makes a nightly catalogue sync a matter of replaying the whole feed.id— ours, returned when the product was created. Supply it to rename a product's SKU. Anidbelonging to another organization returns 404, not somebody else's product.- Neither — always creates.
Also projects the product into the knowledge base and queues it for embedding, so it becomes answerable without a second call. Re-submitting unchanged content is a no-op — the content hash suppresses the re-index — so replaying the feed is cheap as well as correct.
- Body
- application/json
Request body
idstring (uuid)Ours, from a previous response. Only needed to change a product’s
sku— otherwise theskuitself selects what to update. One belonging to another organization is a 404.skustringYour identifier, unique within your organization. Sending one you already have updates that product rather than conflicting with it.
Omit it and one is derived from the name — "Enamel Mug, large" becomes
enamel-mug-large— so a business with no SKUs can still add products, and adding the same name twice updates rather than duplicates. Give products real SKUs once two of them share a name.Max 128 characters
namestringrequiredMax 512 characters
descriptionstringFree text. This is what the AI actually reads and quotes from.
Max 20000 characters
categorystringMax 255 characters
brandstringMax 255 characters
priceAmountintegerMinor units. 13500 is 135.00 in the currency below.
priceCurrencystringISO 4217, e.g. GBP
Min 3 characters · Max 3 characters
availabilityenumDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED· Default"IN_STOCK"stockQuantityintegerattributesobjectArbitrary facts (weight, materials, sizes). Rendered into the AI context.
tagsstring[]Up to 50 items
urlstring (uri)
Response fields 201
skustringYour identifier, unique within your organization. Sending one you already have updates that product rather than conflicting with it.
Omit it and one is derived from the name — "Enamel Mug, large" becomes
enamel-mug-large— so a business with no SKUs can still add products, and adding the same name twice updates rather than duplicates. Give products real SKUs once two of them share a name.Max 128 characters
namestringrequiredMax 512 characters
descriptionstringFree text. This is what the AI actually reads and quotes from.
Max 20000 characters
categorystringMax 255 characters
brandstringMax 255 characters
priceAmountintegerMinor units. 13500 is 135.00 in the currency below.
priceCurrencystringISO 4217, e.g. GBP
Min 3 characters · Max 3 characters
availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED· Default"IN_STOCK"stockQuantityintegerattributesobjectrequiredArbitrary facts (weight, materials, sizes). Rendered into the AI context.
tagsstring[]requiredUp to 50 items
urlstring (uri)idstring (uuid)requiredvariantsobject[]variants[].namestringrequiredvariants[].skustring | nullrequiredvariants[].optionsobjectrequiredvariants[].priceAmountinteger | nullrequiredvariants[].availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED
Product
skustringYour identifier, unique within your organization. Sending one you already have updates that product rather than conflicting with it.
Omit it and one is derived from the name — "Enamel Mug, large" becomes
enamel-mug-large— so a business with no SKUs can still add products, and adding the same name twice updates rather than duplicates. Give products real SKUs once two of them share a name.Max 128 characters
namestringrequiredMax 512 characters
descriptionstringFree text. This is what the AI actually reads and quotes from.
Max 20000 characters
categorystringMax 255 characters
brandstringMax 255 characters
priceAmountintegerMinor units. 13500 is 135.00 in the currency below.
priceCurrencystringISO 4217, e.g. GBP
Min 3 characters · Max 3 characters
availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED· Default"IN_STOCK"stockQuantityintegerattributesobjectrequiredArbitrary facts (weight, materials, sizes). Rendered into the AI context.
tagsstring[]requiredUp to 50 items
urlstring (uri)idstring (uuid)requiredvariantsobject[]variants[].namestringrequiredvariants[].skustring | nullrequiredvariants[].optionsobjectrequiredvariants[].priceAmountinteger | nullrequiredvariants[].availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED
Responses
- 201The stored product.
- 401Missing, malformed, revoked or expired API key.
- 404Not found, or not visible to this organization.
- 422Request failed validation.
- 429Rate limited.
- 500Unexpected error. The correlationId ties it to our logs.
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. Sizes UK 4-13.",
"category": "Footwear",
"brand": "Acme",
"priceAmount": 13500,
"priceCurrency": "GBP",
"availability": "IN_STOCK",
"stockQuantity": 30,
"attributes": {
"sizes": "UK 4-13",
"weight": "640 g"
},
"tags": [
"hiking",
"waterproof"
]
}'{
"sku": "BOOT-TRL-9",
"name": "Trailhead Mid GTX Boot",
"description": "Waterproof mid-cut hiking boot with a Gore-Tex membrane. Sizes UK 4-13.",
"category": "Footwear",
"brand": "Acme",
"priceAmount": 13500,
"priceCurrency": "GBP",
"availability": "IN_STOCK",
"stockQuantity": 30,
"attributes": {
"sizes": "UK 4-13",
"weight": "640 g"
},
"tags": [
"string"
],
"url": "https://acme.example/products/trailhead-mid",
"id": "3f6c2a1e-8b4d-4e7a-9c15-2d0b7e6f4a91",
"variants": [
{
"name": "Trailhead Mid GTX Boot",
"sku": "BOOT-TRL-9",
"options": {},
"priceAmount": 13500,
"availability": "IN_STOCK"
}
]
}Import a catalogue from a CSV file
/products/importUpload the file a shop already has. Every row goes through the same upsert as POST /products, keyed on sku, so re-uploading an updated export changes the products it describes instead of duplicating them.
Columns are matched by name, not by position. SKU, sku and Product Code are the same column; so are Price, Unit Price and Selling Price. Anything not recognised comes back in ignoredColumns rather than being dropped in silence. Only sku and name are required.
Price is what a person types — 13.50, £1,350.00 — and is converted using the currency's own minor unit, so ¥1000 stays ¥1000. Use priceAmount instead to give minor units directly.
A Shopify product export works as-is. Upload the file Shopify gives you: variant and image rows are collapsed into one product each, Body (HTML) is converted to text, stock is summed across variants, and archived becomes DISCONTINUED. Pass ?currency=GBP, since a Shopify export does not carry one.
Rows are independent. A file with three bad rows imports the rest and reports the three, each with its line number. Send ?dryRun=true to validate a file without writing anything — which is what a preview screen should call first.
- Body
- multipart/form-data
Query parameters
dryRunbooleanParse and validate only. Nothing is written.
currencystringISO-4217 code for a file with no currency column — a Shopify export never has one, because a Shopify shop has exactly one currency. Ignored when the file does name one.
Request body
filefilerequiredA CSV file.
Response fields 200
dryRunbooleanrequiredTrue when nothing was written.
totalintegerrequiredData rows in the file, excluding the header.
Min -9007199254740991
importedintegerrequiredMin -9007199254740991
failedintegerrequiredMin -9007199254740991
failuresobject[]requiredfailures[].lineintegerrequired1-based line in the uploaded file, so it can be opened and fixed.
Min -9007199254740991
failures[].skustring | nullrequiredfailures[].messagestringrequiredtruncatedbooleanrequiredfailureswas capped at 100;failedis the real count.ignoredColumnsstring[]requiredHeaders not recognised. A misspelled column shows up here rather than nowhere.
formatenumrequiredWhich shape the file turned out to be.
shopifymeans variant and image rows were collapsed into one product each, sototalcounts products, not lines.One of
generic,shopify
CatalogueImport
dryRunbooleanrequiredTrue when nothing was written.
totalintegerrequiredData rows in the file, excluding the header.
Min -9007199254740991
importedintegerrequiredMin -9007199254740991
failedintegerrequiredMin -9007199254740991
failuresobject[]requiredfailures[].lineintegerrequired1-based line in the uploaded file, so it can be opened and fixed.
Min -9007199254740991
failures[].skustring | nullrequiredfailures[].messagestringrequiredtruncatedbooleanrequiredfailureswas capped at 100;failedis the real count.ignoredColumnsstring[]requiredHeaders not recognised. A misspelled column shows up here rather than nowhere.
formatenumrequiredWhich shape the file turned out to be.
shopifymeans variant and image rows were collapsed into one product each, sototalcounts products, not lines.One of
generic,shopify
Responses
- 200What was imported, and what was not.
- 401Missing, malformed, revoked or expired API key.
- 404Not found, or not visible to this organization.
- 422Request failed validation.
- 429Rate limited.
- 500Unexpected error. The correlationId ties it to our logs.
curl -X POST "https://api.getengageai.app/products/import?dryRun=true" \
-H "Authorization: Bearer $ENGAGE_API_KEY" \
-F "file=@catalogue.csv"{
"dryRun": true,
"total": 0,
"imported": 0,
"failed": 0,
"failures": [
{
"line": 4,
"sku": "BOOT-TRL-9",
"message": "name is required"
}
],
"truncated": true,
"ignoredColumns": [
"string"
],
"format": "generic"
}Get a product
/products/{id}Path parameters
idstring (uuid)required
Response fields 200
skustringYour identifier, unique within your organization. Sending one you already have updates that product rather than conflicting with it.
Omit it and one is derived from the name — "Enamel Mug, large" becomes
enamel-mug-large— so a business with no SKUs can still add products, and adding the same name twice updates rather than duplicates. Give products real SKUs once two of them share a name.Max 128 characters
namestringrequiredMax 512 characters
descriptionstringFree text. This is what the AI actually reads and quotes from.
Max 20000 characters
categorystringMax 255 characters
brandstringMax 255 characters
priceAmountintegerMinor units. 13500 is 135.00 in the currency below.
priceCurrencystringISO 4217, e.g. GBP
Min 3 characters · Max 3 characters
availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED· Default"IN_STOCK"stockQuantityintegerattributesobjectrequiredArbitrary facts (weight, materials, sizes). Rendered into the AI context.
tagsstring[]requiredUp to 50 items
urlstring (uri)idstring (uuid)requiredvariantsobject[]variants[].namestringrequiredvariants[].skustring | nullrequiredvariants[].optionsobjectrequiredvariants[].priceAmountinteger | nullrequiredvariants[].availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED
Product
skustringYour identifier, unique within your organization. Sending one you already have updates that product rather than conflicting with it.
Omit it and one is derived from the name — "Enamel Mug, large" becomes
enamel-mug-large— so a business with no SKUs can still add products, and adding the same name twice updates rather than duplicates. Give products real SKUs once two of them share a name.Max 128 characters
namestringrequiredMax 512 characters
descriptionstringFree text. This is what the AI actually reads and quotes from.
Max 20000 characters
categorystringMax 255 characters
brandstringMax 255 characters
priceAmountintegerMinor units. 13500 is 135.00 in the currency below.
priceCurrencystringISO 4217, e.g. GBP
Min 3 characters · Max 3 characters
availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED· Default"IN_STOCK"stockQuantityintegerattributesobjectrequiredArbitrary facts (weight, materials, sizes). Rendered into the AI context.
tagsstring[]requiredUp to 50 items
urlstring (uri)idstring (uuid)requiredvariantsobject[]variants[].namestringrequiredvariants[].skustring | nullrequiredvariants[].optionsobjectrequiredvariants[].priceAmountinteger | nullrequiredvariants[].availabilityenumrequiredDrives what the AI is allowed to say about getting hold of this.
One of
IN_STOCK,OUT_OF_STOCK,PREORDER,DISCONTINUED
Responses
- 200The product.
- 401Missing, malformed, revoked or expired API key.
- 404Not found, or not visible to this organization.
- 422Request failed validation.
- 429Rate limited.
- 500Unexpected error. The correlationId ties it to our logs.
curl "https://api.getengageai.app/products/$ID" \
-H "Authorization: Bearer $ENGAGE_API_KEY"{
"sku": "BOOT-TRL-9",
"name": "Trailhead Mid GTX Boot",
"description": "Waterproof mid-cut hiking boot with a Gore-Tex membrane. Sizes UK 4-13.",
"category": "Footwear",
"brand": "Acme",
"priceAmount": 13500,
"priceCurrency": "GBP",
"availability": "IN_STOCK",
"stockQuantity": 30,
"attributes": {
"sizes": "UK 4-13",
"weight": "640 g"
},
"tags": [
"string"
],
"url": "https://acme.example/products/trailhead-mid",
"id": "3f6c2a1e-8b4d-4e7a-9c15-2d0b7e6f4a91",
"variants": [
{
"name": "Trailhead Mid GTX Boot",
"sku": "BOOT-TRL-9",
"options": {},
"priceAmount": 13500,
"availability": "IN_STOCK"
}
]
}Delete a product
/products/{id}Soft delete. The product stops being retrievable immediately; historical conversations that referenced it remain readable.
Path parameters
idstring (uuid)required
Response fields 200
deletedtruerequired
Responses
- 200Deleted.
- 401Missing, malformed, revoked or expired API key.
- 404Not found, or not visible to this organization.
- 422Request failed validation.
- 429Rate limited.
- 500Unexpected error. The correlationId ties it to our logs.
curl -X DELETE "https://api.getengageai.app/products/$ID" \
-H "Authorization: Bearer $ENGAGE_API_KEY"{
"deleted": true
}