Skip to main content

Overview

Three endpoints support atomic multi-item inventory operations. Each accepts an items[] array and guarantees all-or-nothing behavior: every item writes in a single database transaction. If item 5 of 10 fails, items 0-4 are automatically rolled back. Each endpoint also accepts single-item (top-level) payloads for backward compatibility.

POST /v1/inventory/receive

When to use

Use for non-PO standalone receipts — opening stock loads, vendor samples, or inventory counts that don’t correspond to a purchase order. Each item establishes its own FIFO cost layer. For PO-linked receipts use POST /v1/purchase-orders/{id}/receive.

Multi-item atomic receive

Response shape

Required fields per item

Validation error shape (400)

If any item fails upfront validation, no items are written and the response includes exact param paths:

POST /v1/inventory/adjustments

When to use

Use for cycle-count corrections, shrinkage write-downs, and inventory revaluations. A GL journal entry fires per item inside the same database transaction (Rule 21). Positive deltas (adding stock) require unit_cost to establish a new FIFO layer. Negative deltas consume existing FIFO layers automatically and calculate COGS from the oldest layers.

Multi-item atomic adjustment

Response shape

Required fields per item

GL behavior

Each adjustment item creates its own journal entry (DR/CR balanced to the cent):
  • Positive delta (add stock): DR Inventory Asset, CR Inventory Adjustment/Suspense. Amount = quantity_delta * unit_cost.
  • Negative delta (remove stock): DR Inventory Adjustment/Shrinkage, CR Inventory Asset. Amount = FIFO COGS (oldest layers consumed first).
The journal_entry_id on each item links the transaction to the GL record. gl_amount is positive for additions, negative for removals.

Serialized product adjustments

For serialized products, include matching serial arrays whose length equals abs(quantity_delta):

POST /v1/inventory/transfers

When to use

Use to move inventory between locations within the same entity (internal transfer) or between entities (intercompany transfer). A transfer is created in draft status. GL entries fire when the transfer is submitted (internal) or shipped (intercompany).

Multi-item atomic transfer

Response shape

Field name aliases

Both naming conventions are accepted:

GL lifecycle


Atomicity guarantee

All three endpoints wrap their items[] loop in a single pool.connect() + BEGIN/COMMIT/ROLLBACK transaction:
  1. All items validate upfront (before BEGIN).
  2. BEGIN starts.
  3. Each item writes inside the transaction.
  4. Any item failure triggers ROLLBACK — every item reverts.
  5. COMMIT fires only after all items succeed.
On failure, the response includes failed_item_index and items_completed_before_failure so you know which item triggered the rollback. All items are reverted regardless of items_completed_before_failure — the count is informational only.

Idempotency

All three endpoints honor the Idempotency-Key header. Replaying the same key within 24 hours returns the original response without re-applying the operation. Use a UUID or a deterministic key tied to your batch (e.g. receive-batch-${date}-${batchId}).
  • POST /v1/purchase-orders/{id}/receive — PO-linked receipts (triggers GRNI GL, matches against PO lines)
  • GET /v1/inventory/balances — query current on_hand / allocated / available by product and location
  • GET /v1/inventory/transactions — movement history log
  • GET /v1/inventory/fifo-layers — FIFO cost layers per product/location
  • POST /v1/inventory/serials — bulk enroll serial numbers (status transition only, no on_hand change)