Stock: set and adjust
Every stock write chooses, per row, between set (the quantity becomes the sent value) and adjust (the sent value is a signed delta added to the quantity on hand). Set is the default, so an existing caller is unchanged.
Use PATCH inventory for live stock
A stock
set or adjust inside a sync job is staged
with the job and applies at close, not on arrival; a staged set
re-bases on-hand stock at that moment. PATCH /v1/catalog/inventory is synchronous:
the quantity changes before it answers.
PATCH /v1/catalog/inventory
The body is { rows: [...] }, 1 to 1,000 rows, at most 1 MiB. A row names
a sku and can carry quantity (an integer), quantityMode (set or adjust), priceMinor, salePriceMinor (null clears it), currency and availability.
Adjust one SKU by +12
curl -X PATCH https://catalog.usethrottle.dev/v1/catalog/inventory \
-H "Authorization: Bearer $CATALOG_ADMIN_KEY" \
-H "Idempotency-Key: delivery-2026-11-01-a" \
-H "Content-Type: application/json" \
-d '{ "rows": [ { "sku": "HR-GUJI-250", "quantity": 12, "quantityMode": "adjust" } ] }'
Set and adjust (SDK)
import { createCatalogAdmin } from '@usethrottle/catalog/admin';
const admin = createCatalogAdmin({ key: process.env.CATALOG_ADMIN_KEY ?? '' });
// Set: the quantity becomes the sent value.
const set = await admin.inventory.set([{ sku: 'HR-GUJI-250', quantity: 40 }]);
// Adjust: a signed delta on the stored quantity (+12 after a delivery, -3
// after an offline sale). The SDK makes an Idempotency-Key when you pass none.
const adjusted = await admin.inventory.adjust(
[
{ sku: 'HR-GUJI-250', quantity: 12 },
{ sku: 'HR-KAA-250', quantity: -3 },
],
{ idempotencyKey: 'delivery-2026-11-01-a' },
);
// One result per row, in order, with the quantity the row left.
for (const result of [...set.results, ...adjusted.results]) {
console.log(result.sku, result.status, result.quantity, result.code);
}
Adjust rules
- Atomic. Two adjusts that arrive together both apply (5 + 3 + 2 = 10); the last writer never wins.
- An Idempotency-Key is required. A request, a job batch or a vendor single batch with an
adjustrow needs anIdempotency-Keyof 1 to 200 characters, or the answer is400 IDEMPOTENCY_KEY_REQUIREDbefore anything is written. A repeated key answers the first answer again and never applies the delta twice. Choose one key per delivery event, not per attempt. Reusing a key with a different body is409 IDEMPOTENCY_CONFLICT. The SDK generates a key when you pass none. - On-hand only. An adjust changes the stored quantity. Stock reserved by open carts is not touched, and availability keeps being computed from reservations as before.
- Below zero. An adjust may take the quantity below zero only when the variant's
backorderssetting isallow. Otherwise the row fails withQUANTITY_BELOW_ZEROand the quantity stays. The check uses the backorders value stored before the row writes: a row that also turns backorders on is still judged against the old value. Nothing is silently clamped to zero. - One mode and one row per SKU per request. The same SKU twice, or set and adjusted together,
fails every row involved (
DUPLICATE_SKU_IN_BATCH,STOCK_MODE_CONFLICT).
Results per row
The answer is { results: [...] }, one entry per row, in order:
| Field | Meaning |
|---|---|
sku | The row’s SKU (null if it had none). |
status | updated, unchanged, not_found (an unknown SKU, or one outside a vendor key’s reach, never SKU_CONFLICT) or invalid. |
quantity | The quantity the row left on hand. Reconcile against this, not your own arithmetic. |
code | Why a row is invalid: QUANTITY_BELOW_ZERO, QUANTITY_OUT_OF_RANGE, QUANTITY_MODE_WITHOUT_QUANTITY, STOCK_MODE_CONFLICT or DUPLICATE_SKU_IN_BATCH. |
sourceWins | false when the caller’s value does not win the quantity (the store key under a value the merchant set by hand). |
A failed row never fails the others. In a job or a vendor single batch, the quantity each stock-writing row left is
recorded and paged from GET …/stock (see reports).