Auth: X-Agent-Key: sq_agt_*. Scopes: read_inventory (reads), write_inventory (writes). Quantity states: available, committed, on_hand, incoming, reserved.

Locations

  • GET /locations · POST /locations · GET /locations/{id} · PUT /locations/{id} · DELETE /locations/{id}

Inventory levels

  • GET /inventory_levels?location_id=&variant_id=
  • POST /inventory_levels/set — { "sku":"TSHIRT-RED-L", "available":10, "location_id":"loc_…" } (or name for another state)
  • POST /inventory_levels/adjust — { "sku":"…", "available_delta":-2 }
  • POST /inventory_levels/move — { "sku":"…", "from":"on_hand"→ rejected; use available↔reserved etc., "quantity":3, "location_id":"…" }
set/adjust accept sku or variant_id and default to the tenant’s default location and the available state. Emits the inventory_levels/update webhook.

Running low

  • GET /low-stock — items running low now: available, incoming (on the way from purchase orders), per_day (units sold a day, last 30 days), days_left, reason (out · units at or under the threshold · days lasts fewer days than the store’s rule), covered (stock on the way fixes it), suggested_quantity (to last 30 days).
  • PUT /low-stock/settings — { "units_below": 5, "days_below": 14, "email_alerts": true } (store-wide; 0 turns a rule off).
  • PUT /low-stock/variants/{variant_id} — { "units_below": 20 } one item’s own threshold (null = the store’s).
  • GET /reorder-suggestions?cover_days=30 — how many to order so stock plus what is on the way lasts that long.
The store team gets one email when an item first runs low (again only after it recovers). The same list is on the Inventory page, Home, the bell and in dtc_store_snapshot.

Purchase orders

  • GET /purchase-orders?status= · GET /purchase-orders/{id} · GET /purchase-orders/suppliers
  • POST /purchase-orders — { "supplier": {"name":"Acme Supply","email":"[email protected]"}, "lines": [{"variant_id":"…","quantity":50,"unit_cost":"4.25"}], "expected_on":"2026-11-30" } → a draft. A Supplier Network supplier is {"network_supplier_id":"…"}; an empty unit_cost uses the item’s known cost.
  • PATCH /purchase-orders/{id} — edit a draft (supplier, lines, location); expected_on/note any time; {"status":"cancelled"}.
  • POST /purchase-orders/{id}/send — emails the supplier from your store’s address (replies come to you) and counts the units as incoming. {"email": false} marks it ordered without an email.
  • POST /purchase-orders/{id}/receive — { "all": true } or { "lines": [{"line_id":"…","quantity":20}] }. Adds the stock at the order’s location (stock history reason received; retry-safe with Idempotency-Key), takes it off incoming, and sets each item’s cost to what this order paid unless "update_costs": false.
Status: draft → sent → partially_received → received, or cancelled.

Bundles

  • GET /bundles · GET /bundles/{variant_id}
  • POST /bundles — { "title":"Sleep kit", "price":"49.00", "items":[{"variant_id":"…","quantity":2},{"variant_id":"…","quantity":1}] } creates a product (draft unless "status":"active").
  • PUT /bundles/{variant_id} — { "items":[…] } changes what is inside, or makes an existing item a bundle; [] turns it back into a normal item.
A bundle has no stock of its own: it can sell as many as its items make, and each sale takes its items from stock (a cancel or restock gives them back). Shoppers see what it contains on the product page and at checkout; the order line lists the items for packing. Its cost is the sum of its items’ costs when they all have one.

Pre-orders

  • GET /preorders · PUT /preorders/{variant_id} — { "ships_on": "2026-11-30" } (or null to stop).
While the date is today or later the item keeps selling when out of stock, and shoppers see “Ships by Nov 30” (your date) on the product page, at checkout and in their order email; the order line is marked as a pre-order. Customers pay when they order. It switches itself off after the date.