Overview
Three endpoints support atomic multi-item inventory operations. Each accepts anitems[] 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.
| Endpoint | Purpose | GL fires? |
|---|---|---|
POST /v1/inventory/receive | Non-PO receipts (FIFO layer creation per item) | No (not PO-linked) |
POST /v1/inventory/adjustments | Cycle counts, shrinkage, revaluations | Yes, per item in same transaction |
POST /v1/inventory/transfers | Inter-location transfers | No on draft; yes on submit |
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 usePOST /v1/purchase-orders/{id}/receive.
Multi-item atomic receive
Response shape
Required fields per item
| Field | Type | Notes |
|---|---|---|
product_id | UUID | Required per item |
quantity | integer | Must be >= 1 |
unit_cost | number | Must be > 0. Establishes the FIFO cost layer. Zero-cost receives are rejected. |
location_id | UUID | Per-item override; falls back to root location_id |
notes | string | Optional. Per-item notes on the transaction record. |
Validation error shape (400)
If any item fails upfront validation, no items are written and the response includes exactparam 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) requireunit_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
| Field | Type | Notes |
|---|---|---|
product_id | UUID | Required per item |
quantity_delta | integer | Non-zero signed integer. Positive = add stock, negative = remove stock. |
unit_cost | number | Required when quantity_delta > 0 (new FIFO layer). Ignored for negative deltas (FIFO auto-resolves). |
location_id | UUID | Per-item override; falls back to root location_id |
reason | string | Per-item override; falls back to root reason. Common values: cycle_count, shrinkage, revaluation, manual. |
notes | string | Optional. Combined with memo in the transaction record. |
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).
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 equalsabs(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 indraft 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:| Canonical | Alias |
|---|---|
source_location_id | from_location_id |
dest_location_id | to_location_id |
GL lifecycle
| Status | GL action |
|---|---|
draft | No GL. Items stored; no stock movement yet. |
submitted (internal) | GL fires: DR Transit Inventory / CR Source Warehouse Inventory. |
shipped (intercompany) | GL fires: DR Due From / CR Source Inventory. |
received (intercompany) | GL fires: DR Dest Inventory / CR Due To. |
Atomicity guarantee
All three endpoints wrap theiritems[] loop in a single pool.connect() + BEGIN/COMMIT/ROLLBACK transaction:
- All items validate upfront (before
BEGIN). BEGINstarts.- Each item writes inside the transaction.
- Any item failure triggers
ROLLBACK— every item reverts. COMMITfires only after all items succeed.
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 theIdempotency-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}).
Related endpoints
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 locationGET /v1/inventory/transactions— movement history logGET /v1/inventory/fifo-layers— FIFO cost layers per product/locationPOST /v1/inventory/serials— bulk enroll serial numbers (status transition only, no on_hand change)

