Inventory

Per (variant × location) stock with 6 distinct states, TTL'd reservations, and an immutable movement ledger.

Stock states

on_hand        physical units in the warehouse
- committed    units reserved for paid orders awaiting fulfillment
- reserved     units held by active checkouts (TTL'd)
- damaged      units flagged unsellable
- safety_stock manual buffer (e.g. show as out-of-stock at <10)
= available    GENERATED ALWAYS column the storefront sees
+ incoming     on-the-way from supplier (info only)

available = on_hand - committed - reserved - damaged - safety_stock is a Postgres GENERATED ALWAYS column — Postgres recomputes it on every write. The storefront filters on available to decide what to show as in-stock.

Locations

A location is a place stock can sit: warehouse, retail, POS, fulfillment center, dropship.

curl -X POST "$API/locations.json" \
  -H "Authorization: Bearer $NEVIOS_KEY" -H "Content-Type: application/json" \
  -d '{
    "handle": "praha-warehouse",
    "name": "Praha sklad",
    "type": "warehouse",
    "address1": "Plzeňská 8",
    "city": "Praha",
    "country_code": "CZ",
    "is_default": true,
    "fulfillment_priority": 50
  }'

A location can have served_market_ids — if set, it only fulfills orders for those markets. Useful for region-bound warehouses.

Adjust stock

curl -X PUT "$API/inventory/{variant_id}/{location_id}/adjust.json" \
  -H "Authorization: Bearer $NEVIOS_KEY" -H "Content-Type: application/json" \
  -d '{
    "state": "on_hand",
    "delta": 50,
    "type": "received",
    "reason_code": "PO-1234",
    "reason_text": "Purchase order received"
  }'

Every adjustment writes to the immutable inventory_movements ledger (10 movement types: reserved | released | fulfilled | received | adjusted | transferred_in/out | damaged | restocked | quality_control). The ledger is append-only — UPDATE/DELETE blocked by trigger.

Reservations

When a checkout is created (default commerce.checkout.reserve_at = checkout_enter), each line tries to reserve units at the highest-priority active location with stock:

{
  "inventory_reservation": {
    "id": "308...",
    "variant_id": "...",
    "location_id": "...",
    "quantity": 2,
    "owner_type": "checkout",
    "owner_id": "<checkout_id>",
    "status": "active",
    "expires_at": "2026-05-01T11:30:00Z",
    "reserved_at": "2026-05-01T10:30:00Z"
  }
}

Reservation lifecycle:

active → consumed   (checkout completed → committed → fulfilled)
       → released   (checkout abandoned / explicit release)
       → expired    (TTL passed; cleanup cron sweeps these)

Configure reservation timing per account:

Setting valueBehavior
cart_addReserve when item is added to cart (longest hold; max guarantee)
checkout_enter (default)Reserve when cart converts to checkout
paymentReserve only when start_payment is called (shortest hold; risk of last-second oversells)

Endpoints

VerbPath
POST/{handle}/locations.json
GET/{handle}/locations.json
PUT/{handle}/inventory/{variant_id}/{location_id}/adjust.json
GET/{handle}/inventory/{variant_id}/{location_id}.json
GET/{handle}/variants/{variant_id}/inventory.json
POST/{handle}/inventory/reservations.json
DELETE/{handle}/inventory/reservations/{reservation_id}.json

Next: Customers.