Skip to content

Inventory

Stock is tracked per variant (and at the item level for items without variants). Every row exposes three quantities plus a derived status. Examples reuse the api() helper.

FieldMeaning
onHandPhysical units in stock
reservedUnits held (e.g. for pending orders)
availableonHand − reserved — what a storefront can sell
stockStatusIN_STOCK · LOW_STOCK · OUT_OF_STOCK (derived)
lowStockThresholdAt/below available, status becomes LOW_STOCK (0 disables)

Read stock

// Public — a storefront checking availability for a PDP
const rows = await api<Inventory[]>(`/public/inventory/${itemId}`);
 
// Merchant — filterable, enriched with item name/slug
const { rows: m } = await api<{ rows: Inventory[] }>(
  "/merchant/inventory?stockStatus=LOW_STOCK", { token },
);
 
// Merchant — counts by status for a dashboard
const summary = await api("/merchant/inventory/summary", { token });
// → { IN_STOCK, LOW_STOCK, OUT_OF_STOCK, total }

Mutations

All mutations are POST /merchant/inventory/:id/<action> where :id is the inventory row id. Mutations require inventory:adjust; Owner/Admin retain broad access, and Staff needs that group.

// Adjust on-hand by a signed delta (receiving stock, shrinkage, corrections)
await api(`/merchant/inventory/${id}/adjust`, { method: "POST", token,
  body: JSON.stringify({ delta: 24, reason: "Received PO #1182" }) });
 
// Hold units (moves available → reserved). Rejects if quantity > available.
await api(`/merchant/inventory/${id}/reserve`, { method: "POST", token,
  body: JSON.stringify({ quantity: 2, reason: "manual hold" }) });
 
// Release a hold (reserved → available). Rejects if quantity > reserved.
await api(`/merchant/inventory/${id}/release`, { method: "POST", token,
  body: JSON.stringify({ quantity: 2 }) });
 
// Low-stock alarm. 0 disables it. Recomputes stockStatus immediately.
await api(`/merchant/inventory/${id}/threshold`, { method: "POST", token,
  body: JSON.stringify({ lowStockThreshold: 5 }) });

adjust rejects a delta that would push available negative. reason is required on adjust (1–200 chars) and optional on reserve/release — it's written into the audit log so stock movements are traceable.