Inventory webhook events

Updated September 10, 20263 min read

An inventory webhook fires once for every inventory position that changes: one inventory key at one location. The body carries the position before and after the change and the operation that produced it. The trigger HTTP header on every delivery names the single inventory trigger.

Trigger

Trigger valueFires when
ALL_INVENTORY_UPDATESAn inventory position is created, updated or deleted

There is no way to subscribe to a subset of changes. Every counter change in the tenant fires the webhook; filter by inventory_key or location_id in your handler.

When it fires

  • Bulk import: once per row that changed a counter, sent when the row is applied, not when the import call accepts it. The first event for a new position fires one CREATE with old_image null; every later change fires UPDATE. A row that leaves the position unchanged is silent.
  • Orders: every reservation change fires an UPDATE, as orders are created, updated, cancelled, fulfilled and shipped.
  • Delete: deleting an inventory position through the API fires a DELETE with new_image null.

Carriyo sends nothing while the tenant has no active inventory webhook.

Payload

The body is an envelope with the position before and after the change. Field names are snake_case.

{
  "old_image": {
    "tenant": "YOUR_TENANT_ID",
    "inventory_key": "YOUR_PRODUCT_REF",
    "location_id": "ACCOUNT_f2b82334-96d6-43bb-987d-ca36c2305824",
    "on_hand": 40,
    "reserved": 0,
    "unavailable": 2,
    "creation_date": "2026-09-09T18:20:37.847Z",
    "update_date": "2026-09-09T18:20:37.847Z",
    "creation_source": { "source_type": "api", "request_type": "INVENTORY_EVENTS_BULK_IMPORT", "event_type": "SNAPSHOT", "reason": "Nightly stock sync" },
    "update_source": { "source_type": "api", "request_type": "INVENTORY_EVENTS_BULK_IMPORT", "event_type": "SNAPSHOT", "reason": "Nightly stock sync" }
  },
  "new_image": {
    "tenant": "YOUR_TENANT_ID",
    "inventory_key": "YOUR_PRODUCT_REF",
    "location_id": "ACCOUNT_f2b82334-96d6-43bb-987d-ca36c2305824",
    "on_hand": 38,
    "reserved": 0,
    "unavailable": 2,
    "creation_date": "2026-09-09T18:20:37.847Z",
    "update_date": "2026-09-09T18:21:07.279Z",
    "creation_source": { "source_type": "api", "request_type": "INVENTORY_EVENTS_BULK_IMPORT", "event_type": "SNAPSHOT", "reason": "Nightly stock sync" },
    "update_source": { "source_type": "api", "request_type": "INVENTORY_EVENTS_BULK_IMPORT", "event_type": "SALE", "reason": "POS sale 4471" }
  },
  "trigger": "ALL_INVENTORY_UPDATES",
  "operation": "UPDATE"
}
  • operation is CREATE, UPDATE or DELETE.
  • old_image is null on CREATE; new_image is null on DELETE.
  • Each image is one inventory position: one inventory_key at one location_id. A change across several locations arrives as several deliveries.
  • on_hand, reserved and unavailable are the position's three counters. available is not in the payload; compute it as on_hand - reserved - unavailable.
  • update_source says what caused the change: event_type is the inventory event type, and reason is the reason the sender put on the row. For order-driven changes, references inside update_source carries the order_id, fulfillment_order_id, shipment_id or return_request_id.
  • The batch request_id is not delivered. To tie a delivery back to a bulk import, correlate through update_source.reason and update_source.event_type, so put something you can match on in the reason you send.
Note

The inventory payload uses snake_case field names (old_image, new_image). This differs from the order envelope, which uses camelCase (oldImage, newImage).

Headers

Every delivery carries event-id, trigger and webhook-id, plus any headers set on the configuration. Retries add retry-count and last-retry. Carriyo does not sign deliveries; use the configuration's authentication settings and your own endpoint authentication.

Retries

A response outside the 2xx range, or a timeout, schedules a retry: immediately, then 3 and 5 minutes later. With extended retries enabled on the configuration, five more follow at 1, 3, 5, 8 and 13 hours.

Scoping

Inventory webhooks are tenant-scoped, not merchant-scoped. A configuration receives changes for every inventory key at every location in the tenant; Carriyo replaces any merchants you send with _ANY, which means all merchants.

Configuration

Create the webhook configuration in the Dashboard, or with POST /webhooks (create-webhook) in the Carriyo Core API. Set entity_type to INVENTORY and notify_status to exactly ["ALL_INVENTORY_UPDATES"]; any other list is rejected. Authentication towards your endpoint is set on the configuration; see Configurations.

{
  "name": "Inventory sync",
  "entity_type": "INVENTORY",
  "notify_status": ["ALL_INVENTORY_UPDATES"],
  "endpoint_url": "https://your-system.com/webhook/inventory",
  "active": true
}