UpdateInventory sets absolute on-hand quantities.
You call thisinventory:self
const url = 'https://api.zentail.com/v2/inventory';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"updates":[{"sku":"example","warehouseUniqueId":"example","quantity":1,"binLocation":"example"}],"observedTs":"2026-04-15T12:00:00Z"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.zentail.com/v2/inventory \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "updates": [ { "sku": "example", "warehouseUniqueId": "example", "quantity": 1, "binLocation": "example" } ], "observedTs": "2026-04-15T12:00:00Z" }'Idempotent and order-insensitive: replaying a call changes nothing, and an update whose observed_ts predates what Zentail already holds is discarded and reported as stale rather than applied. A slow retry overtaking a newer reading cannot roll the count backwards.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
Server-capped per call. Exceeding the cap fails the request with the limit in the error rather than silently truncating.
object
The Zentail SKU whose quantity this sets, exactly as ListInventory vends it — matching is exact, including case. A SKU Zentail’s catalog does not carry is rejected with UNKNOWN_SKU and the rest of the batch still applies.
Which of the caller’s warehouses this quantity is in, named with the identifier ListWarehouses vends. Required: one SKU can be stocked in several warehouses and an update sets exactly one of them, so omitting it does not mean “everywhere”. A warehouse the integration does not own is rejected with WAREHOUSE_NOT_YOURS.
Absolute on-hand, not a delta. What is physically there right now, excluding anything already picked for an order.
Optional free-text bin or slot, passed through for operator reference.
When the caller observed these quantities — not when it sent them. Required: without it Zentail cannot tell a stale retry from a fresh reading, and ordering is the whole basis of the idempotency guarantee.
Examplegenerated
{ "updates": [ { "sku": "example", "warehouseUniqueId": "example", "quantity": 1, "binLocation": "example" } ], "observedTs": "2026-04-15T12:00:00Z"}Responses
Section titled “ Responses ”A successful response.
object
One entry per update. Order is not guaranteed, which is why each result echoes its full key.
InventoryUpdateResult echoes the full key of the update it answers. SKU alone is not enough — a batch may carry the same SKU for two warehouses.
object
The SKU this result answers for, echoed from the update that produced it.
The warehouse this result answers for, echoed from the update. Both halves of the key are echoed because a batch may carry the same SKU for two warehouses, so the SKU alone does not say which update a result belongs to.
True when Zentail applied the update. False covers a real rejection and a
discarded out-of-order reading alike, so read stale before treating false
as a fault.
Human-facing detail for a rejection, for logs and support conversations. Empty on success. Do not branch on it — the wording is not stable; branch on failure_reason instead.
True when the update was discarded because observed_ts predated the value Zentail already held. Not an error: the newer reading stands. Treat as success unless it is happening constantly, which means clock skew or a second writer.
A stale result reports success = false with failure_reason UNSPECIFIED, so check stale before failure_reason or a normal discard reads as a fault.
UpdateFailureReason classifies a rejected InventoryUpdate. UNKNOWN_SKU is the one an integration is expected to see in bulk and should not alert on: the integration’s catalog is not Zentail’s. Anything else is a real fault.
- UPDATE_FAILURE_REASON_UNKNOWN_SKU: The SKU is not in Zentail’s catalog for this account.
- UPDATE_FAILURE_REASON_WAREHOUSE_NOT_YOURS: warehouse_unique_id does not name a warehouse this integration owns.
- UPDATE_FAILURE_REASON_MISSING_SKU: The update carried no SKU.
- UPDATE_FAILURE_REASON_INTERNAL: Zentail failed to apply the update. Retryable; read error_message.
- UPDATE_FAILURE_REASON_INVALID: The update is permanently unacceptable for a reason not named above — a negative quantity, say. Do not retry it unchanged; read error_message. Use INTERNAL instead when a retry could succeed.
Example
{ "results": [ { "failureReason": "UPDATE_FAILURE_REASON_UNSPECIFIED" } ]}default
Section titled “default”An unexpected error response.
object
object
Examplegenerated
{ "code": 1, "message": "example", "details": [ { "@type": "example" } ]}