Skip to main content

Catalog Webhooks

Thyris supports both inbound and outbound catalog webhooks.

  • Inbound webhooks let external systems push product updates into Thyris.
  • Outbound webhooks let Thyris notify merchant systems when catalog, cart, checkout, or order events happen.

For required and optional webhook fields, see 05-request-field-requirements.md.

Inbound Webhook Endpoint​

POST /webhooks/catalog
Authorization: Bearer tr_live_your_key_here
Content-Type: application/json

Production URL:

https://webhooks.thyris.cloud/webhooks/catalog

Thyris validates the developer API key, enforces store scope, and processes accepted catalog payloads according to the documented webhook contract.

Inbound Events​

EventPurpose
product.createdCreate a product.
product.updatedUpdate a product.
product.upsertedCreate or update a product using SKU/external ID.
product.deletedDelete a product using SKU/external ID.
product.inventory.updatedUpdate product stock fields.
product.price.updatedUpdate product price fields.
catalog.batch.upsertedCreate or update multiple products.
catalog.batch.deletedDelete multiple products.
cart.createdCreate a cart from inbound webhook payload.
cart.item.added / cart.bundle.addedAdd one or more items to an existing open cart.
cart.item.updated / cart.bundle.updatedReplace quantities for one or more existing cart lines.
cart.item.removed / cart.bundle.removedRemove one or more cart lines.
cart.archivedArchive an existing cart permanently.
order.completedComplete an order from an existing checkout.

Create/update/upsert product events require name and price. Product delete events identify products by sku and/or externalId within the store. Cart mutation events require cart.cartId; add events require line catalogId and quantity, while update/remove events accept line itemId or catalogId.

For product.inventory.updated, inventoryQuantity is an absolute current-stock snapshot, not a decrement command. If order completion has already reduced a local stock copy, set the value to the merchant snapshot; do not subtract it again. Include a stable source event ID and source version/timestamp so duplicate or older snapshots can be ignored.

Inbound and outbound catalog webhook envelopes may include optional top-level version and timestamp. When omitted on inbound requests, Thyris normalizes metadata for processing. Outbound deliveries always include version and timestamp; receivers should store them with the deduplication record and may reject older source snapshots according to merchant policy.

Inbound Single Product Upsert​

{
"event": "product.upserted",
"source": "shopify",
"product": {
"storeId": "STORE_UUID",
"sku": "HOODIE-BLK",
"externalId": "shopify_product_123",
"name": "Black Hoodie",
"description": "A heavyweight black hoodie.",
"price": 49.9,
"currency": "USD",
"status": "active",
"inStock": true,
"inventoryQuantity": 12,
"imageUrl": "https://cdn.example.com/hoodie.png",
"imageUrls": [
"https://cdn.example.com/hoodie.png",
"https://cdn.example.com/hoodie-back.png"
],
"productUrl": "https://store.example.com/products/hoodie",
"otherDetails": {
"brand": "Acme",
"category": "Apparel"
},
"variants": [
{
"sku": "HOODIE-BLK-M",
"externalId": "shopify_variant_123",
"name": "Black Hoodie / Medium",
"price": 49.9,
"currency": "USD",
"status": "active",
"inStock": true,
"inventoryQuantity": 8,
"otherDetails": {
"color": "black",
"size": "M"
}
}
]
}
}

Inbound Batch Upsert​

{
"event": "catalog.batch.upserted",
"source": "pim",
"products": [
{
"storeId": "STORE_UUID",
"sku": "SKU-001",
"externalId": "pim_001",
"name": "Product 1",
"price": 19.9,
"currency": "USD"
},
{
"storeId": "STORE_UUID",
"sku": "SKU-002",
"externalId": "pim_002",
"name": "Product 2",
"price": 29.9,
"currency": "USD"
}
]
}

Maximum batch size is 250 products.

Inbound Delete​

{
"event": "product.deleted",
"source": "erp",
"product": {
"storeId": "STORE_UUID",
"sku": "HOODIE-BLK",
"externalId": "shopify_product_123"
}
}

If the product is not found, the delete request is treated as ignored rather than a hard failure.

Inbound Cart Mutation Examples​

Add a bundle to an existing cart:

{
"event": "cart.bundle.added",
"source": "merchant-oms",
"version": "merchant.cart.v2@2026-09-12T18:00:00Z",
"timestamp": "2026-09-12T18:00:00Z",
"cart": {
"cartId": "CART_UUID",
"items": [
{ "catalogId": "1234567890", "quantity": 1 },
{ "catalogId": "9876543210", "quantity": 2 }
]
}
}

Archive an existing cart:

{
"event": "cart.archived",
"source": "merchant-oms",
"cart": {
"cartId": "CART_UUID"
}
}

Archived carts are not restored by webhook, API, MCP, or dashboard operation.

Inbound Response​

{
"success": true,
"mode": "updated",
"count": 1,
"data": {
"id": "product_uuid"
},
"results": [
{
"success": true,
"mode": "updated",
"data": {
"id": "product_uuid"
}
}
]
}

mode can be created, updated, deleted, ignored, or batch.

Outbound Webhook Destinations​

Outbound destinations are configured in the Merchant Dashboard under the store's catalog integrations area.

Required fields:

  • Name
  • Public HTTPS destination URL
  • Selected events

Optional field:

  • Secret header

Private network targets and non-HTTPS URLs are rejected.

Catalog destinations only receive catalog events selected for that destination. Procurement destinations are configured separately.

Outbound Events​

Catalog writes performed through MCP are not silent. The catalog engine dispatches the same configured outbound webhooks as REST/dashboard flows, with direction: "outbound" and source: "mcp".

EventTrigger
product.createdProduct created through API, dashboard, or catalog_upsert_product.
product.updatedProduct updated through API, dashboard, or catalog_upsert_product.
product.upsertedGeneric upsert event where configured.
product.deletedProduct deleted, including through catalog_delete_product.
product.inventory.updatedStock quantity or stock status changed.
product.price.updatedPrice changed.
catalog.batch.upsertedBatch upsert event.
catalog.batch.deletedBatch delete event.
cart.createdCart created, including through catalog_create_cart or cart_create.
cart.item.addedOne item added after cart creation.
cart.item.updatedOne cart line quantity updated.
cart.item.removedOne cart line removed.
cart.bundle.addedMultiple items added in one atomic bundle mutation.
cart.bundle.updatedMultiple cart lines updated in one atomic bundle mutation.
cart.bundle.removedMultiple cart lines removed in one atomic bundle mutation.
cart.updatedCompatibility event emitted after item or bundle mutation.
cart.clearedCart cleared, including through catalog_clear_cart or cart_clear.
cart.closedOpen cart closed without checkout.
cart.archivedCart archived permanently.
checkout.createdCheckout ID created for a cart, including checkout preparation.
checkout.cancelledPrepared checkout cancelled.
order.createdOrder created, including through catalog_complete_order.
order.updatedOrder changed.
order.completedOrder completed, including through catalog_complete_order.
order.cancelledOrder cancelled.

Outbound Headers​

Content-Type: application/json
x-webhook-secret: optional_shared_secret

x-webhook-secret is sent only when a secret is configured for the destination.

Outbound Product Payload​

{
"event": "product.updated",
"direction": "outbound",
"source": "api",
"version": "catalog.v1@2026-09-12T18:00:00Z",
"timestamp": "2026-09-12T18:00:00Z",
"product": {
"scope": {
"merchantId": "MERCHANT_UUID",
"storeId": "STORE_UUID"
},
"sku": "HOODIE-BLK",
"externalId": "shopify_product_123",
"name": "Black Hoodie",
"price": 44.9,
"currency": "USD",
"status": "active",
"inStock": true,
"inventoryQuantity": 24,
"imageUrls": [
"https://cdn.example.com/hoodie.png",
"https://cdn.example.com/hoodie-back.png"
]
}
}

For MCP-triggered delivery, the same payload uses "source": "mcp". A destination receives the event only when that event is selected in its configuration.

MCP Event Matrix​

MCP ToolOutbound Events
catalog_upsert_productproduct.created or product.updated
catalog_delete_productproduct.deleted
catalog_create_cart / cart_createcart.created, and checkout creation when the compatibility cart-create flow prepares checkout immediately
catalog_mutate_cart_items, cart_add_item, cart_update_item, cart_remove_itemcart.item.added, cart.item.updated, cart.item.removed, plus cart.updated
cart_add_bundle, cart_update_bundle, cart_remove_bundlecart.bundle.added, cart.bundle.updated, cart.bundle.removed, plus cart.updated
catalog_clear_cart / cart_clearcart.cleared
catalog_archive_cart / cart_archivecart.archived
cart_closecart.closed
checkout_preparecheckout.created
checkout_cancelcheckout.cancelled
catalog_complete_orderorder.created, order.completed, and product.inventory.updated for affected products

Outbound Order Payload​

{
"event": "order.completed",
"direction": "outbound",
"source": "api",
"order": {
"id": "order_uuid",
"orderId": "ord_1234567890abcdef12345678",
"scope": {
"merchantId": "merchant_uuid",
"storeId": "store_uuid"
},
"cart": {
"id": "cart_uuid"
},
"checkout": {
"id": "chk_1234567890abcdef12345678"
},
"payment": {
"id": "pay_1234567890abcdef12345678",
"status": "paid"
},
"status": "completed",
"totalQuantity": 2,
"totalAmount": "99.80",
"currency": "USD",
"items": []
}
}

Delivery Behavior​

  • Thyris sends outbound webhooks as HTTP POST.
  • Destination must respond within 10 seconds.
  • Any 2xx status is treated as success.
  • Non-2xx, timeout, DNS, TLS, URL validation, or network errors are logged as failed delivery.
  • Delivery logs are visible in the dashboard.

Receiver Recommendations​

  • Make receivers idempotent by event type and product/order identifiers.
  • Return 2xx quickly and process heavier work asynchronously.
  • Validate x-webhook-secret if configured.
  • Do not rely on event delivery order across different destinations.
  • Keep the endpoint public HTTPS.