Adjustments correct a SKU's stock at a location, with an auditable business reason (shrinkage, damage, count correction, etc.). All endpoints for this resource require the inventory:adjust permission.
The adjustment applies the stock change WHEN CREATED, not when approved
Contrary to what the approve/reject naming suggests, creating an adjustment already modified the stock. Approval is, in practice today, a no-op on stock — it only changes the status field. Rejection does have an effect: it reverts the stock. Read the full "Write semantics" section before building your approval flow.
reason accepts 11 values — 'sale' is NOT one of them
The public endpoint validates reason against this exact list: count_correction, damage, theft, expired, found, return, supplier_error, restock, shrinkage, sample, other. The value sale exists in the data model but is reserved for the automatic decrement the system generates when processing an order — passing reason: "sale" to this endpoint responds 400 bad-request/invalid-reason.
Key points about creation:
You do not set oldQuantity yourself. The server always reads the current stock from the inventory record at the moment the adjustment is created, ignoring any oldQuantity you send in the body.
adjustmentQuantity is calculated server-side as newQuantity − oldQuantity.
If you omit productSku, the server resolves it in this order: (1) the productSku already present on the inventory record for that SKU/location, (2) a lookup in the product catalog, (3) falls back to using the sku itself as a last resort.
This is the most important business pattern of this resource:
Creation (POST /inventory/adjustments) applies the stock change immediately. The record is born with status: 'pending_review', but the stock has already been modified at that point — not when someone approves it. This POSTdoes trigger the Inventory Updated event (and a possible Low Stock Alert), because internally it uses the same write path as PUT for stock.
approve on a pending_review adjustment (today's normal flow) does not touch stock again. It only changes status → approved and stamps the audit fields (approvedBy, approvedAt). The stock already moved in step 1.
reject does have an effect on stock: it reverts it to oldQuantity (triggering another Inventory Updated event for the reversal), and it also automatically creates a second adjustment record: reason: 'count_correction', sourceType: 'reversal', sourceId pointing to the original adjustment, adjustedBy: 'system', already in approved status.
Rejecting an adjustment creates TWO records, not one
If you list adjustments expecting to see only the one you rejected, you'll see two: the original (now status: 'rejected') and a synthetic reversal adjustment generated by the system. Both count in GET /inventory/adjustments and in /summary.