ReferenceFulfillment & inventory

Fulfillment & inventory

The pick/pack/ship loop has a supply-and-routing spine covering two areas: purchase orders, receiving, and physical stock locations on the inventory side, and fulfillment nodes, routing modes, dropship vendor costs, and COGS capture on the fulfillment side.

Inventory: purchase orders, receiving, locations

Inventory (items/lots/FEFO reservations) has supply-side depth: vendors, purchase orders (with lines), receiving against a PO, and per-node physical locations. Receiving fails closed on a non-receivable PO (po_not_receivable) or an over-receipt (receive_exceeds_expected).

Inventory administration (operator-managed)

Tenant-level location, vendor, and purchase-order administration is operator/console-managed. Merchants manage their own through the merchant routes below.

MethodPathPurpose
POST/v1/tenants/:tenant/locationsCreate a physical stock location; optional node_ref binds it to a fulfillment node.
GET/v1/tenants/:tenant/locationsList locations.
POST/v1/tenants/:tenant/vendorsCreate a supply vendor.
GET/v1/tenants/:tenant/vendorsList vendors.
POST/v1/tenants/:tenant/purchase-ordersCreate a PO + its lines.
GET/v1/tenants/:tenant/purchase-ordersList POs.
GET/v1/tenants/:tenant/purchase-orders/:idFetch a PO + lines.
POST/v1/tenants/:tenant/purchase-orders/:id/receiveReceive stock against a PO (creates lots; fails closed on over-receipt).

Merchant routes — fdk_, scope manage:own_inventory, under /v1/merchant/inventory

MethodPathPurpose
GET/v1/merchant/inventory/vendorsList the caller’s vendors.
POST/v1/merchant/inventory/vendorsCreate a vendor.
GET/v1/merchant/inventory/purchase-ordersList POs.
GET/v1/merchant/inventory/purchase-orders/:idFetch one PO + lines.
POST/v1/merchant/inventory/purchase-ordersCreate a PO.
POST/v1/merchant/inventory/purchase-orders/:id/receiveReceive against a PO.

Fulfillment: nodes, modes, dropship costs, COGS

A fulfillment node is a own_warehouse / threepl / vendor endpoint. Allocation picks a mode:

  • dedicated — draw from own stock, no node pinned.
  • allocated — pin the queue to a specific own_warehouse/threepl node and draw FEFO from that node’s bound inventory location. An unbound node fails closed (node_location_unmapped) rather than silently drawing from elsewhere.
  • dropship — route each line to a vendor node without reserving or decrementing own stock, recording a per-line vendor dispatch (the “purchase/dispatch to vendor”) and a dropship shipment row that stands in for the carrier label.

Mode/node selection happens on the first (queued) pass only; a re-entrant allocate keeps the row’s chosen mode. Mixing own-stock and dropship on one route is refused (wrong_mode) — dropship has its own route.

COGS capture: every shipped line captures cost of goods at ship time (when the reservations commit / the vendor is dispatched). Dropship COGS resolves each line’s unit cost from the (node, sku) vendor-cost mapping; a line with no cost recorded is captured at zero rather than blocking the ship.

Fulfillment administration (operator-managed)

Tenant-level fulfillment administration (queue, nodes, vendor costs, allocation, labels, ship/deliver) is operator/console-managed. Merchants manage their own through the merchant routes below.

MethodPathPurpose
GET/v1/tenants/:tenant/fulfillmentList the fulfillment queue.
GET/v1/tenants/:tenant/fulfillment/:idFetch one queue row.
POST/v1/tenants/:tenant/fulfillment/:id/allocateAllocate own stock; a nodeId pins mode allocated, omitting it is dedicated.
POST/v1/tenants/:tenant/fulfillment/:id/dropshipRoute to a vendor node (mode dropship), no own-stock reservation.
POST/v1/tenants/:tenant/nodesCreate a fulfillment node (own_warehouse/threepl/vendor).
GET/v1/tenants/:tenant/nodesList nodes.
POST/v1/tenants/:tenant/vendor-costsMap a per-(node, sku) vendor unit cost (dropship COGS input).
GET/v1/tenants/:tenant/vendor-costsList vendor costs.
POST/v1/tenants/:tenant/fulfillment/:id/labelBuy a shipping label.
POST/v1/tenants/:tenant/fulfillment/:id/shipShip — commit reservations / dispatch vendor, capture COGS, advance the order.
POST/v1/tenants/:tenant/fulfillment/:id/cancelCancel the queue row.
POST/v1/tenants/:tenant/fulfillment/:id/deliverMark delivered (advances the order to delivered).

Merchant routes — fdk_, scope manage:own_fulfillment, under /v1/merchant/fulfillment

MethodPathPurpose
GET/v1/merchant/fulfillmentList the queue.
GET/v1/merchant/fulfillment/nodesList nodes.
POST/v1/merchant/fulfillment/nodesCreate a node.
GET/v1/merchant/fulfillment/vendor-costsList vendor costs.
POST/v1/merchant/fulfillment/vendor-costsMap a vendor cost.
GET/v1/merchant/fulfillment/:idFetch one queue row.
POST/v1/merchant/fulfillment/:id/allocateAllocate.
POST/v1/merchant/fulfillment/:id/dropshipDropship-route.
POST/v1/merchant/fulfillment/:id/labelBuy a label.
POST/v1/merchant/fulfillment/:id/shipShip.
POST/v1/merchant/fulfillment/:id/cancelCancel.
POST/v1/merchant/fulfillment/:id/deliverMark delivered.

Manifest gates

None. Fulfillment routing, POs, receiving, nodes, and COGS are back-office surfaces with no storefront manifest toggle.

Notes

  • Allocation matches by SKU — a fulfillment queue line whose SKU has no matching inventory item fails allocation with a surfaced 422 inventory_item_not_found, never a silent skip.
  • One queue row and one shipment per order today; reships and multi-package shipments are out of scope.
  • Dropship COGS for a line with no vendor cost recorded is captured at zero, not blocked — a documented simplification.