Inventory webhook events
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 value | Fires when |
|---|---|
ALL_INVENTORY_UPDATES | An 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
CREATEwithold_imagenull; every later change firesUPDATE. 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
DELETEwithnew_imagenull.
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"
}
operationisCREATE,UPDATEorDELETE.old_imageisnullonCREATE;new_imageisnullonDELETE.- Each image is one inventory position: one
inventory_keyat onelocation_id. A change across several locations arrives as several deliveries. on_hand,reservedandunavailableare the position's three counters.availableis not in the payload; compute it ason_hand - reserved - unavailable.update_sourcesays what caused the change:event_typeis the inventory event type, andreasonis thereasonthe sender put on the row. For order-driven changes,referencesinsideupdate_sourcecarries theorder_id,fulfillment_order_id,shipment_idorreturn_request_id.- The batch
request_idis not delivered. To tie a delivery back to a bulk import, correlate throughupdate_source.reasonandupdate_source.event_type, so put something you can match on in thereasonyou send.
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
}