Update Purchase Order
PUT /purchase_orders/{id}
🚀 Paid plan only
Updates a purchase order. Also accepts PATCH.
state is a plain attribute assignment with no inclusion check, so only
ever send draft, active or completed - any other value persists and
hides the purchase order from every state filter. Moving to active has
server-side effects: it creates stock entries for every variant at the
destination warehouse and is what enables the supplier emails.
received_quantity on a line item is a DELTA, not a target: stored
received becomes previous + received_quantity, warehouse physical
stock is adjusted by the delta, the variant cost price is recalculated
and received_at is stamped. It is NOT idempotent and there is no
reverse, so send it once and never retry it automatically. For a purchase
order in a currency other than the company’s, set currency_rate
explicitly - it defaults to 0 and the cost-price recalculation multiplies
by it.
Line items are edited by id and added by omitting the id. There is no
_destroy: use Delete Purchase Order Line Item. Add only ONE new line
item per request - a second line for a variant already on the purchase
order is dropped silently.
Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Purchase order ID.
Example
491Header Parameters
Section titled “Header Parameters ”E.g. 123
Example
123Request Body
Section titled “Request Body ”object
object
Supplier ID. Required; must belong to the company.
Warehouse ID stock is delivered to. Required; must belong to the company.
Lifecycle state: draft -> active -> completed.
PO number. Auto-generated if blank.
Must be unique per company.
Expected delivery date (must fall between 2010-01-01 and 2035-01-01).
Default GBP. Cannot be changed once the PO is active.
Recalculated as whole days from created_at to expected_date on any save that changes either date, overwriting a value sent in the same request. Sent on its own it is stored as given. Send expected_date and omit this.
DEAD FIELD - it is stored and wired to nothing. Use send_supplier_report_email.
Emails the supplier the purchase order on save, but ONLY when the purchase order is (or becomes) active - a draft never emails, silently. Delivery is also suppressed entirely for some companies (multi-channel accounts, and sellers who have not used the standalone app) and the response looks identical, so never report it as sent.
NOT validated, so any string is accepted - deliberately left unconstrained here. Only pdf or csv attaches a document; every other value, and omitting it, sends the email with no attachment (unlike the reminder path, which defaults to pdf).
Line items.
object
Existing line item ID (update requests only).
Must exist in the company and not be deleted. Required for create requests.
Must be greater than 0. Required for create requests.
Unit cost. Must be greater than 0. Required for create requests.
Required for create requests.
Updates the supplier product variant reference.
Updates the supplier product variant title.
Update requests only — increments the received count for the line item. Cannot be negative.
Example
{ "purchase_order": { "state": "active", "note": "Updated note", "purchase_order_product_variants_attributes": [ { "id": 1575, "received_quantity": 5 } ] }}Responses
Section titled “ Responses ”OK. Returns the updated purchase order. NOTE: a COMPLETED purchase order also answers 200 while applying NOTHING - every column is blocked and nested line items are dropped silently, so read state before offering an edit.
object
object
Decimal serialized as a JSON string. Coerce before arithmetic. Defaults to 0. Set it explicitly for a purchase order in a currency other than the company’s: receiving multiplies the cost price by it.
object
Whole days from created_at to expected_date, recalculated on any save that changes either date. A value you send is kept only until expected_date next changes, so treat it as derived and drive it with expected_date.
The line items. This is the key - there is no line_items field on a purchase order.
object
Unit cost. Decimal serialized as a JSON string. Coerce before arithmetic.
Line item id. Pass it to the line-item sub-routes.
The SELLABLE id, the same id the sellables endpoints return. No translation is needed in either direction.
Units ordered.
Units received so far. Set indirectly by sending received_quantity as a delta, and never settable directly.
Always the purchase order’s supplier - a sent value is overwritten.
Decimal serialized as a JSON string. Coerce before arithmetic.
Decimal serialized as a JSON string. Coerce before arithmetic.
Decimal serialized as a JSON string. Coerce before arithmetic.
object
Decimal serialized as a JSON string. Coerce before arithmetic.
Decimal serialized as a JSON string. Coerce before arithmetic.
The same supplier shape as the purchase order’s own supplier.
object
object
object
object
object
object
object
object
Unique per company when present. A duplicate is a 400.
Decimal serialized as a JSON string. Coerce before arithmetic.
Only these three values are ever returned. The received-status values accepted by the list state filter are filter-only inputs.
Computed on read, never stored. Decimal serialized as a JSON string. Coerce before arithmetic.
object
The template used to render this supplier’s purchase order document, on a plan with customizable templates.
Reminder and report emails go here. Can be blank, and the send still reports success.
Write-only: there is no stored column, so List and Get always return null. A Create or Update response echoes the value sent in that same request.
Decimal serialized as a JSON string. Coerce before arithmetic.
Use this for the purchase order value, computed on read and never stored. Decimal serialized as a JSON string. Coerce before arithmetic.
Decimal serialized as a JSON string. Coerce before arithmetic.
Validation failed. currency_code and supplier_id cannot be changed once the PO is active (“can’t update on state active”). On UPDATE the line-item messages are prefixed “Purchase order product variants …”.
The company’s plan does not include purchase orders, or the user lacks the manage_purchase_orders permission. Body is {"error_messages":[...]}. Checked before the body is read.
Purchase order not found (or belongs to another company). Also returned INSTEAD OF a 400 when a changed supplier_id, destination_warehouse_id or any line-item product_variant_id does not resolve in the company.