Skip to content

Reference

Catalog Push API

Keep the assistant’s catalog in sync from your backend: upsert products in batches of up to 500 and remove the ones you no longer sell.

All endpoints require Authorization: Bearer sk_live_… and Content-Type: application/json. Products pushed here live in a dedicated “Push API” source; pushes are incremental (products you don’t send are left untouched).

Verify a key

GET/v1/catalog/ping

Returns the shop the key belongs to. Connectors call it during setup.

200 OKjson
{ "ok": true, "shopId": "3bbb53e1-5814-4251-95a6-ad1ada36f015", "shopName": "Lumière Beauty" }

Upsert products

POST/v1/catalog/products:batchUpsert

Creates or updates up to 500 products per request, matched by external_id (+ locale). Each item is validated on its own: invalid items are reported in errors while the rest is saved. Body limit: 10 MB.

Request body

FieldTypeDescription
productsrequiredProduct[]1–500 products (see below).
localestringDefault locale for items without one: en ro it fr de es. Defaults to the shop’s default language.

Product object

FieldTypeDescription
external_idrequiredstring ≤255Your stable id. Product pages and add-to-cart events use it.
namerequiredstring ≤500Product name.
skustringSKU / MPN.
parent_external_idstringGroups variants of the same product.
descriptionstring ≤20000Plain text or HTML (converted to text).
urlhttp(s) URLProduct page.
image_urlsURL[] ≤20First image is shown on product cards.
pricenumberRegular price (rounded to 2 decimals).
sale_pricenumberDiscounted price, if any.
currencyISO 4217e.g. EUR, RON. Defaults to the shop currency.
in_stockbooleanDefault true. Out-of-stock products are never recommended.
stock_qtyinteger ≥0Optional quantity.
categoriesstring[] ≤50Category paths, e.g. "Face > Serums".
brandstringBrand or manufacturer.
attributesobjectFree-form facts used for search and answers: string, number, boolean or string[] values.
localestringLanguage of this item’s texts.
source_updated_atISO 8601When the product last changed in your system (optional).
Requestbash
curl -X POST "https://api.askmerra.com/v1/catalog/products:batchUpsert" \
  -H "Authorization: Bearer $ASKMERRA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locale": "en",
    "products": [{
      "external_id": "LUM-SE-01",
      "sku": "LUM-SE-01",
      "name": "Hydra Glow Serum 30 ml",
      "description": "Multi-weight hyaluronic acid serum for dehydrated skin.",
      "url": "https://lumiere-beauty.ro/p/lum-se-01",
      "image_urls": ["https://lumiere-beauty.ro/img/LUM-SE-01.jpg"],
      "price": 39, "sale_price": 32, "currency": "EUR",
      "in_stock": true, "stock_qty": 64,
      "categories": ["Face > Serums"], "brand": "Lumière",
      "attributes": { "skin_type": ["dry", "sensitive"], "vegan": true }
    }]
  }'
200 OKjson
{
  "received": 1,
  "created": 1,
  "updated": 0,
  "unchanged": 0,
  "failed": 0,
  "errors": []
}

updated counts content and price/stock changes. Content changes (name, description, attributes…) trigger re-enrichment for those products only; price and stock changes are live immediately. Each error has index, external_id and message.

Remove products

POST/v1/catalog/products:batchDelete

Deactivates up to 1,000 products by external_id. They stop being recommended immediately; sending them again reactivates them.

FieldTypeDescription
external_idsrequiredstring[]1–1000 ids.
localestringOnly deactivate this language version. Omit to deactivate all languages.
200 OKjson
{ "deleted": 2 }

Example: sync from Node.js

sync.tsts
const API = 'https://api.askmerra.com/v1';
const headers = { Authorization: `Bearer ${process.env.ASKMERRA_SECRET_KEY}`, 'Content-Type': 'application/json' };

export async function pushProducts(products: object[]) {
  for (let i = 0; i < products.length; i += 500) {
    const res = await fetch(`${API}/catalog/products:batchUpsert`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ products: products.slice(i, i + 500) }),
    });
    if (res.status === 429) {
      await new Promise((r) => setTimeout(r, Number(res.headers.get('retry-after') ?? 5) * 1000));
      i -= 500; // retry this batch
      continue;
    }
    const result = await res.json();
    if (result.failed) console.warn('rejected items', result.errors);
  }
}

Always send the full product

An upsert replaces the stored product with what you send, so omitted fields are cleared. For real-time stock or price updates, send the complete product again. Only changes to name, description, categories, brand or attributes trigger re-enrichment, so frequent price/stock pushes are cheap.