Skip to content
engage-ai

List products

GET/products

Cursor-paginated, newest first.

Query parameters

  • cursorstring

    From a previous response. Opaque.

  • limitinteger

    Default 25, max 100.

    Max 100

Response fields 200
  • itemsProduct[]required
  • nextCursorstring | nullrequired

    Opaque. Pass back verbatim as cursor. Null means the end.

  • hasMorebooleanrequired

Product

  • skustring

    Your 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

  • namestringrequired

    Max 512 characters

  • descriptionstring

    Free text. This is what the AI actually reads and quotes from.

    Max 20000 characters

  • categorystring

    Max 255 characters

  • brandstring

    Max 255 characters

  • priceAmountinteger

    Minor units. 13500 is 135.00 in the currency below.

  • priceCurrencystring

    ISO 4217, e.g. GBP

    Min 3 characters · Max 3 characters

  • availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED · Default "IN_STOCK"

  • stockQuantityinteger
  • attributesobjectrequired

    Arbitrary facts (weight, materials, sizes). Rendered into the AI context.

  • tagsstring[]required

    Up to 50 items

  • urlstring (uri)
  • idstring (uuid)required
  • variantsobject[]
  • variants[].namestringrequired
  • variants[].skustring | nullrequired
  • variants[].optionsobjectrequired
  • variants[].priceAmountinteger | nullrequired
  • variants[].availabilityenumrequired

    Drives 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.
Request
curl "https://api.getengageai.app/products" \
  -H "Authorization: Bearer $ENGAGE_API_KEY"
Response · 200
{
  "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

POST/products

Which 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. An id belonging 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 the sku itself selects what to update. One belonging to another organization is a 404.

  • skustring

    Your 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

  • namestringrequired

    Max 512 characters

  • descriptionstring

    Free text. This is what the AI actually reads and quotes from.

    Max 20000 characters

  • categorystring

    Max 255 characters

  • brandstring

    Max 255 characters

  • priceAmountinteger

    Minor units. 13500 is 135.00 in the currency below.

  • priceCurrencystring

    ISO 4217, e.g. GBP

    Min 3 characters · Max 3 characters

  • availabilityenum

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED · Default "IN_STOCK"

  • stockQuantityinteger
  • attributesobject

    Arbitrary facts (weight, materials, sizes). Rendered into the AI context.

  • tagsstring[]

    Up to 50 items

  • urlstring (uri)
Response fields 201
  • skustring

    Your 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

  • namestringrequired

    Max 512 characters

  • descriptionstring

    Free text. This is what the AI actually reads and quotes from.

    Max 20000 characters

  • categorystring

    Max 255 characters

  • brandstring

    Max 255 characters

  • priceAmountinteger

    Minor units. 13500 is 135.00 in the currency below.

  • priceCurrencystring

    ISO 4217, e.g. GBP

    Min 3 characters · Max 3 characters

  • availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED · Default "IN_STOCK"

  • stockQuantityinteger
  • attributesobjectrequired

    Arbitrary facts (weight, materials, sizes). Rendered into the AI context.

  • tagsstring[]required

    Up to 50 items

  • urlstring (uri)
  • idstring (uuid)required
  • variantsobject[]
  • variants[].namestringrequired
  • variants[].skustring | nullrequired
  • variants[].optionsobjectrequired
  • variants[].priceAmountinteger | nullrequired
  • variants[].availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED

Product

  • skustring

    Your 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

  • namestringrequired

    Max 512 characters

  • descriptionstring

    Free text. This is what the AI actually reads and quotes from.

    Max 20000 characters

  • categorystring

    Max 255 characters

  • brandstring

    Max 255 characters

  • priceAmountinteger

    Minor units. 13500 is 135.00 in the currency below.

  • priceCurrencystring

    ISO 4217, e.g. GBP

    Min 3 characters · Max 3 characters

  • availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED · Default "IN_STOCK"

  • stockQuantityinteger
  • attributesobjectrequired

    Arbitrary facts (weight, materials, sizes). Rendered into the AI context.

  • tagsstring[]required

    Up to 50 items

  • urlstring (uri)
  • idstring (uuid)required
  • variantsobject[]
  • variants[].namestringrequired
  • variants[].skustring | nullrequired
  • variants[].optionsobjectrequired
  • variants[].priceAmountinteger | nullrequired
  • variants[].availabilityenumrequired

    Drives 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.
Request
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"
    ]
  }'
Response · 201
{
  "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

POST/products/import

Upload 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

  • dryRunboolean

    Parse and validate only. Nothing is written.

  • currencystring

    ISO-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

  • filefilerequired

    A CSV file.

Response fields 200
  • dryRunbooleanrequired

    True when nothing was written.

  • totalintegerrequired

    Data rows in the file, excluding the header.

    Min -9007199254740991

  • importedintegerrequired

    Min -9007199254740991

  • failedintegerrequired

    Min -9007199254740991

  • failuresobject[]required
  • failures[].lineintegerrequired

    1-based line in the uploaded file, so it can be opened and fixed.

    Min -9007199254740991

  • failures[].skustring | nullrequired
  • failures[].messagestringrequired
  • truncatedbooleanrequired

    failures was capped at 100; failed is the real count.

  • ignoredColumnsstring[]required

    Headers not recognised. A misspelled column shows up here rather than nowhere.

  • formatenumrequired

    Which shape the file turned out to be. shopify means variant and image rows were collapsed into one product each, so total counts products, not lines.

    One of generic, shopify

CatalogueImport

  • dryRunbooleanrequired

    True when nothing was written.

  • totalintegerrequired

    Data rows in the file, excluding the header.

    Min -9007199254740991

  • importedintegerrequired

    Min -9007199254740991

  • failedintegerrequired

    Min -9007199254740991

  • failuresobject[]required
  • failures[].lineintegerrequired

    1-based line in the uploaded file, so it can be opened and fixed.

    Min -9007199254740991

  • failures[].skustring | nullrequired
  • failures[].messagestringrequired
  • truncatedbooleanrequired

    failures was capped at 100; failed is the real count.

  • ignoredColumnsstring[]required

    Headers not recognised. A misspelled column shows up here rather than nowhere.

  • formatenumrequired

    Which shape the file turned out to be. shopify means variant and image rows were collapsed into one product each, so total counts 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.
Request
curl -X POST "https://api.getengageai.app/products/import?dryRun=true" \
  -H "Authorization: Bearer $ENGAGE_API_KEY" \
  -F "file=@catalogue.csv"
Response · 200
{
  "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

GET/products/{id}

Path parameters

  • idstring (uuid)required
Response fields 200
  • skustring

    Your 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

  • namestringrequired

    Max 512 characters

  • descriptionstring

    Free text. This is what the AI actually reads and quotes from.

    Max 20000 characters

  • categorystring

    Max 255 characters

  • brandstring

    Max 255 characters

  • priceAmountinteger

    Minor units. 13500 is 135.00 in the currency below.

  • priceCurrencystring

    ISO 4217, e.g. GBP

    Min 3 characters · Max 3 characters

  • availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED · Default "IN_STOCK"

  • stockQuantityinteger
  • attributesobjectrequired

    Arbitrary facts (weight, materials, sizes). Rendered into the AI context.

  • tagsstring[]required

    Up to 50 items

  • urlstring (uri)
  • idstring (uuid)required
  • variantsobject[]
  • variants[].namestringrequired
  • variants[].skustring | nullrequired
  • variants[].optionsobjectrequired
  • variants[].priceAmountinteger | nullrequired
  • variants[].availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED

Product

  • skustring

    Your 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

  • namestringrequired

    Max 512 characters

  • descriptionstring

    Free text. This is what the AI actually reads and quotes from.

    Max 20000 characters

  • categorystring

    Max 255 characters

  • brandstring

    Max 255 characters

  • priceAmountinteger

    Minor units. 13500 is 135.00 in the currency below.

  • priceCurrencystring

    ISO 4217, e.g. GBP

    Min 3 characters · Max 3 characters

  • availabilityenumrequired

    Drives what the AI is allowed to say about getting hold of this.

    One of IN_STOCK, OUT_OF_STOCK, PREORDER, DISCONTINUED · Default "IN_STOCK"

  • stockQuantityinteger
  • attributesobjectrequired

    Arbitrary facts (weight, materials, sizes). Rendered into the AI context.

  • tagsstring[]required

    Up to 50 items

  • urlstring (uri)
  • idstring (uuid)required
  • variantsobject[]
  • variants[].namestringrequired
  • variants[].skustring | nullrequired
  • variants[].optionsobjectrequired
  • variants[].priceAmountinteger | nullrequired
  • variants[].availabilityenumrequired

    Drives 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.
Request
curl "https://api.getengageai.app/products/$ID" \
  -H "Authorization: Bearer $ENGAGE_API_KEY"
Response · 200
{
  "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

DELETE/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.
Request
curl -X DELETE "https://api.getengageai.app/products/$ID" \
  -H "Authorization: Bearer $ENGAGE_API_KEY"
Response · 200
{
  "deleted": true
}