📦 Product Data Webhook API

Custom Integration Documentation

📋 Overview

This API lets 3rd party systems push product catalog data into our system. When products are created, updated, or removed in your shop, send them to our webhook endpoint and we build the chatbot's product catalog and knowledge library from them — the same way our native Shoptet / Shopify integrations do.

⚠️ Important: This endpoint supports two event types: product:update (create or update products) and product:delete (remove products). Any other event type returns a 400 Bad Request.

🚀 Getting Started

1. Get Your Webhook URL

Contact your system administrator to obtain your unique webhook URL. It shares the same webhook-key as your order-data channel. It looks like:

https://admin.jarabot.com/webhooks/product_update/{your-webhook-key}

2. Webhook Endpoint

POST /webhooks/product_update/{webhook_key}

📝 Request Format

Headers

Header Value Required
Content-Type application/json Yes
Authorization Token {your-api-key} If enabled

The Authorization header is required only when API-key validation is enabled for your connection. We strongly recommend enabling it for product push.

🟢 Event: product:update

The payload is an array of product objects. Each object is the full current state of one product, keyed by its sku — we replace any previously stored data for that SKU (no field-level merge). The example below mixes one fully-described product and one minimal (required-fields-only) product.

Request Body

{
  "webhook": {
    "event": "product:update",
    "payload": [
      {
        "sku": "NIKE-PEGASUS-40",
        "external_id": "12345",
        "title": "Nike Air Zoom Pegasus 40",
        "url": "https://shop.example.cz/nike-pegasus-40",
        "price": 3499.00,
        "currency": "CZK",
        "margin": 800.00,
        "availability": 0,
        "availability_description": "In stock",
        "visible": true,
        "short_description": "Versatile running shoe for daily training.",
        "description": "<p>The Pegasus 40 brings snappier response and a lighter upper.</p>",
        "image_url": "https://shop.example.cz/img/pegasus40.jpg",
        "thumbnail_url": "https://shop.example.cz/img/pegasus40-thumb.jpg",
        "brand": "Nike",
        "categories": [
          { "title": "Shoes",         "url": "https://shop.example.cz/shoes" },
          { "title": "Sports shoes",   "url": "https://shop.example.cz/sports-shoes" },
          { "title": "Running shoes",  "url": "https://shop.example.cz/running-shoes" }
        ],
        "traits": {
          "color":   ["black"],
          "surface": ["road"]
        },
        "variants": [
          { "sku": "NIKE-PEGASUS-40-42", "availability": 0, "params": { "size": "42" } },
          { "sku": "NIKE-PEGASUS-40-43", "availability": 5, "params": { "size": "43" } }
        ],
        "gifts": ["Free sports socks"],
        "ean": "0195869884532",
        "last_modified": "2026-06-30T10:00:00Z"
      },
      {
        "sku": "GEL-NIMBUS-26",
        "title": "Asics Gel-Nimbus 26",
        "url": "https://shop.example.cz/gel-nimbus-26",
        "price": 4290,
        "currency": "CZK",
        "availability": 0
      }
    ]
  }
}

📊 Product Object Structure

Required fields

The minimum viable product is these six fields. It will index and be findable, but answers are thin without a description, image, and traits — send the recommended fields too.

Field Type Required Description
sku String Yes Stable unique product identifier. Used as the upsert/dedup key.
title String Yes Product name.
url String Yes Product page URL (http/https).
price Number Yes Final customer price including VAT, in currency. No conversion is applied.
currency String Yes ISO-4217 3-letter code (e.g. "CZK", "EUR").
availability Integer Yes Days until available: 0 = in stock now, -1 = unavailable / out of stock, N = available in N days. An out-of-stock product stays in the catalog and is shown to the customer as unavailable; send visible: false to take it out. A missing availability is read as 0, so always send it.

Recommended fields

Field Type Required Description
external_id String No Your stable product id if different from sku. Defaults to sku.
description String No Long description. HTML allowed (stripped for catalog, kept for the knowledge library). Primary content for AI answers.
short_description String No Teaser / summary.
image_url String No Main product image URL. Used in product cards.
thumbnail_url String No Thumbnail image URL. Defaults to image_url.
brand String No Manufacturer / brand name. Searchable and facetable.
categories Array<Object> No Category breadcrumb, broad → specific. See Categories below.
availability_description String No Human-readable stock text shown to the customer (e.g. "In stock", "Ships in 3 days").
traits Object No Structured filterable attributes. See Traits below.
variants Array<Object> No Per-variant rows (size/color combos). See Variants below.

Optional fields

Field Type Required Description
visible Boolean No Default true. false removes the product from the catalog and the knowledge library. We keep no copy of it, so make the product visible again by pushing it with visible: true.
last_modified String No When the product last changed on your side (ISO-8601). Send it. Without it we rewrite and re-index the product's library document on every push, which makes a repeated full-catalog push much slower and more expensive. With it we skip a document that has not changed.
margin Number No Price minus purchase price, in currency. Internal only, never shown to customers. Defaults to 0.
purchase_price Number No Alternative to margin: we compute margin = price − purchase_price.
ean / gtin String No Barcode. Indexed — a customer who pastes a barcode into the chat finds the product. A barcode match never outranks a word match.
gifts Array<String> No Free items bundled with the purchase. Shown as a "Free gifts" block.

Categories (breadcrumb)

An ordered array, broadest first and the most specific (leaf) last.

The whole path is the product's category, not the leaf name alone. Two categories named "Running shoes" under two different parents stay two categories. Every prefix of the path filters: a customer who narrows to "Shoes" still finds a product filed under "Shoes / Sports shoes / Running shoes". Send the same path for every product in a category, or the two spellings become two categories.

"categories": [
  { "title": "Shoes",        "url": "https://shop.example.cz/shoes" },
  { "title": "Sports shoes", "url": "https://shop.example.cz/sports-shoes" },
  { "title": "Running shoes","url": "https://shop.example.cz/running-shoes" }
]
Field Type Required Description
categories[].title String Yes Category name for this breadcrumb level.
categories[].url String No Link to this category's page. We store the URL of the last (leaf) category only, and use it to link an applied category filter to your category page. URLs on the higher levels are accepted and ignored.

Traits (filterable attributes)

An object mapping attribute name to an array of string values — always an array, even for a single value. Used for faceted filtering ("red shoes size 42"). Only send values that are actually buyable.

"traits": {
  "color": ["red"],
  "size":  ["42", "43", "44"],
  "material": ["leather"]
}

Variants

Optional per-variant rows. A buyable variant's params are folded into the product's filterable traits, so a variant-only attribute (e.g. shoe size) becomes searchable even when it isn't a top-level trait.

"variants": [
  { "sku": "SHOE-42", "availability": 0, "params": { "size": "42" } },
  { "sku": "SHOE-43", "availability": 5, "params": { "size": "43" } }
]
Field Type Required Description
variants[].sku String No Variant SKU (distinct from the parent product sku). Accepted for your own reference; we do not store it. Only params and availability shape the catalog.
variants[].availability Integer No Same convention as product availability. Decides whether the variant is buyable. Defaults to the product's availability.
variants[].params Object No Attribute name → single value for this variant (e.g. { "size": "42" }).

📦 Batch size and rate

One request carries as many products as you want, and we answer 200 before we write them. The write itself is not free: each product updates the catalog and rewrites its library document.

For a first import of a large catalog, tell us before you start so we can watch the queues.

🔴 Event: product:delete

The payload is an array of SKUs to remove. Deleted products disappear from the catalog and knowledge library.

{
  "webhook": {
    "event": "product:delete",
    "payload": ["GEL-NIMBUS-26", "NIKE-PEGASUS-40"]
  }
}

📤 Response Codes

200 OK: The batch was accepted and queued for processing. Per-product validation happens asynchronously; invalid products are skipped and logged while valid ones in the same batch still persist.
400 Bad Request:
401 Unauthorized: Missing or invalid Authorization token (when API-key validation is enabled)
404 Not Found: Invalid webhook key

🔄 Update, Create & Delete Behavior

Note: sku is the unique identifier. Keep it consistent across all webhook calls for the same product. Products are language-agnostic — one pushed product feeds every language the bot is configured for.

🔀 Migrating from an XML product feed

If your bot reads its products from an XML feed today (Heureka, Google), the switch to push is a swap, and we do it in one window with you. What you decide first:

The two sources cannot run side by side. While the feed is still configured, a nightly rebuild from the feed deletes every product that only the push knows about. So the order is: you push the full catalog, we remove the feed the same day. Plan one window, and keep the feed URL — it is what we restore from if we have to roll back.

💡 Example Implementations

cURL — update products

curl -X POST https://admin.jarabot.com/webhooks/product_update/your-webhook-key \
  -H "Content-Type: application/json" \
  -H "Authorization: Token your-api-key" \
  -d '{
    "webhook": {
      "event": "product:update",
      "payload": [
        {
          "sku": "PROD-001",
          "title": "Example Product",
          "url": "https://shop.example.com/prod-001",
          "price": 599.00,
          "currency": "CZK",
          "availability": 0
        }
      ]
    }
  }'

cURL — delete products

curl -X POST https://admin.jarabot.com/webhooks/product_update/your-webhook-key \
  -H "Content-Type: application/json" \
  -H "Authorization: Token your-api-key" \
  -d '{
    "webhook": {
      "event": "product:delete",
      "payload": ["PROD-001", "PROD-002"]
    }
  }'

Last updated: 24 August 2026