Skip to main content

Receive Shipment

POST 

/api/inbound-shipments/receive

Receive goods against a purchase order shipment. Creates a receipt record and triggers inventory movements.

Required scope: purchase-orders:write

Grant this scope to your token under Settings → Developer → Personal Access Tokens.

Authentication: Requires Bearer token.

Async behavior:

  • By default, this endpoint dispatches the receive to a background job chain and returns 202 Accepted with data.tracked_job_log_id. The frontend should open the tracked job log endpoints (notifyJobStarted) and watch lastCompletedJob to refresh.
  • Pass ?async=0 to force the legacy synchronous response (200 with the receipt resource).
  • The async path is only taken when the payload is ad-hoc eligible: requires purchase_order_id, and rejects any payload that includes unexpected_items or negative receipt_lines[].quantity. An explicit inbound_shipment_id or force_new_shipment now takes the async path as well — the receipt is landed on the chosen shipment. Ineligible payloads silently fall back to the sync path.
  • 409 Conflict is returned when a background job is already in flight (PENDING or PROCESSING) for the same purchase_order_id — the caller should tell the user to track progress via the tracked job log endpoints instead of retrying.

Fields:

  • purchase_order_id (conditional): Required if purchase_order_shipment_id is not provided. Required for the async path.
  • purchase_order_shipment_id (optional): Specific shipment ID to receive. If omitted, receives for the entire PO
  • received_at (required): Date/time of receipt
  • warehouse_id (optional): Override destination warehouse
  • shipment_date (optional): Used if a new shipment needs to be created automatically
  • shipping_method_id (optional): Shipping method for auto-created shipment
  • tracking (optional): Tracking number for auto-created shipment, max 255 characters
  • inbound_shipment_id (optional, exists:inbound_shipments,id): Route the whole receive against this specific inbound shipment. Restricts delegation to that shipment and over-receives it past its planned capacity if the received quantity exceeds what was expected. Routes the whole receive against this shipment; handled on the async path.
  • force_new_shipment (optional, boolean): If true, skip auto-routing/delegation to existing planned shipments and create a brand-new ad-hoc inbound shipment for this receipt. Mutually exclusive in intent with inbound_shipment_id
  • receipt_lines (optional): Array of specific line quantities to receive. If omitted, receives all unreceived lines
  • receipt_lines[].purchase_order_shipment_line_id (conditional): Shipment line ID. Required without purchase_order_line_reference or purchase_order_line_id
  • receipt_lines[].purchase_order_line_id (conditional): PO line ID. Alternative to shipment_line_id
  • receipt_lines[].purchase_order_line_reference (conditional): PO line reference code. Alternative to IDs
  • receipt_lines[].quantity (required): Quantity received. Negative quantities force sync fallback (the reversal/adjustment path).
  • unexpected_items (optional): Items received that were not on the original PO. Presence forces sync fallback.
  • unexpected_items[].product_id (conditional): Product ID. Required without product_sku
  • unexpected_items[].product_sku (conditional): Product SKU. Required without product_id
  • unexpected_items[].quantity (required): Quantity of unexpected item
  • unexpected_items[].action (optional): Action for unexpected item (e.g., add_to_inventory)
  • unexpected_items[].amount (optional): Unit cost for unexpected item
  • unexpected_items[].description (optional): Description for unexpected item

lots (synchronous responses only). A 200 response reports the lots this receipt created, each with its fifo_layer_id and batch_number. Use it to attach a Certificate of Analysis or other batch paperwork to the lot after the receipt commits, via POST /api/v2/fifo-layers/{fifoLayer}/documents. The default asynchronous path answers 202 with a tracked job and no lots, so request ?async=0 when you intend to file documents against what you receive. A line received without a batch number still produces a lot, but its batch is generated server-side (AUTO-…).

  • rechain_affected (optional bool, default false) — when the entry is dated earlier than existing consumption in an affected product/warehouse pool, opt in to re-anchor those later transactions to the oldest available stock (true FIFO) after the save commits. Runs as a background job; pools in a locked accounting period or with unsupported shapes are skipped and reported.

Serial numbers: for a serial-tracked product, a line may carry serials (one per unit received). An empty list is accepted. A non-empty list must match the line quantity (in Auto generation mode it may be shorter, and the rest are generated), must not repeat a serial, and must not name a serial that already exists. Otherwise the request returns 422 with a message naming the serial.

Requires permission: purchase_orders.receive

Request​

Responses​

OK

Response Headers
    Content-Type