Skip to main content

Update Sales Order

PUT 

/api/sales-orders/:salesOrder

Updates a sales order. Accepts a partial payload — only the provided fields are applied.

Required scope: orders:write

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

Authentication: Requires Bearer token.

Common use cases:

  • Reopen a closed order: send {"order_status": "open", "force_reopen": true}
  • Close an open order: send {"order_status": "closed"}
  • Cancel an order: send {"canceled_at": "2026-05-08T12:00:00Z"}
  • Update lines, addresses, or other fields by including only the keys you want to change.

Reopen behavior (force_reopen):

  • A closed order is normally not reopened by order_status: open if its fulfillment_status is fulfilled or over_fulfilled. This guard suppresses spurious reopens from sales-channel sync paths (e.g. Walmart Seller-Fulfilled "Shipped") that would leave the order stuck in open after updateFulfillmentStatus runs.
  • Manual UI reopens express explicit user intent and pass force_reopen: true to bypass the guard.

Selected request body fields (all optional unless noted):

  • order_status (string): One of draft, open, reserved, closed, cancelled, refunded, completed.
  • force_reopen (bool, default false): When true, allow reopening a closed order even if it is fulfilled / over-fulfilled. Only meaningful when order_status: open is sent on a currently closed order.
  • fulfillment_status (string): unfulfilled, partially_fulfilled, fulfilled, over_fulfilled, out_of_sync.
  • payment_status (string|null): unpaid, partially_paid, paid, refunded.
  • canceled_at (date-time|null): Cancel the order by setting; uncancel by setting to null.
  • sales_order_number (string): Must be unique among the orders on the same sales channel. Renaming an order onto a number another order already uses on that channel returns 422; the same number may legitimately exist on a different sales channel. Re-sending an order's own current number is always accepted. When sales_channel_id is sent in the same request, uniqueness is checked against that channel.
  • customer_po_number (string|null), payment_date (date|null), fulfilled_at (date-time|null), tracking_number (string|null).
  • order_date, ship_by_date, hold_until_date, deliver_by_date — calendar-day fields. Send either a plain YYYY-MM-DD date or a full timestamp. A plain date means that calendar day in your account timezone: from an account on America/New_York, 2026-09-23 is stored as 2026-09-23T04:00:00Z and reads back as 23 September in every report and export. Send a full timestamp only when you mean a specific instant — it is stored exactly as given. hold_until_date is the "do not ship before" release date used for preorders and holds.
  • payment_term_id (int|null): Payment term for the order; must reference a sales-applicable, non-archived payment term. When null, the customer's default payment term (or the account-wide sales default) applies.
  • payment_due_date (date|null): Manual payment due-date override (YYYY-MM-DD). When omitted, the due date is derived from the effective payment term and the order date.
  • store_id (int|null), sales_channel_id (int), shipping_method_id (int|null), customer_id (int|null), sales_rep_id (int|null), sales_rep_name (string|null), shipping_address_id (int|null), billing_address_id (int|null), currency_id (int), currency_code (string|null), tax_rate_id (int|null).
  • sub_sales_channel_id (int|null): Moves the order into a sub-sales channel, so its COGS, revenue and returns post to that sub-sales channel's nominal code overrides — for example, re-classifying a channel order as a reshipment, a warranty replacement or an affiliate seeding order. The sub-sales channel must belong to the order's sales channel (or to the sales_channel_id sent in the same request); otherwise the request returns 422. Changing it after the order has shipped re-posts the order's existing fulfillment, credit and return entries to the new accounts. Later updates from the sales channel keep the sub-sales channel you set. Send null to clear it. List sub-sales channels with GET /api/sub-sales-channels.
  • requested_shipping_method (string|null), packing_slip_printed_at (date-time|null), last_synced_from_sales_channel_at (date-time|null), is_tax_included (bool|null).
  • tags (array|null), discount_lines (array|null), sales_order_lines (array of SalesOrderLineData), financial_lines (array of FinancialLineData), custom_field_values (array), notes (array of NoteData), customer (CustomerData|null), shipping_address (AddressData|null), billing_address (AddressData|null).
  • Quote fields: is_quote (bool), memo_for_customer (string|null), quote_sent_at (date-time|null), quote_approved_by_customer_at (date-time|null), quote_expires_at (date-time|null).
  • Behavioral flags: shouldDeleteFulfillments (bool, default false), shippingAddressChanged (bool, default false), on_hold (bool, default false), deleteFulfillmentsReason (string).

Tax on lines:

  • The order's tax_rate_id is the default rate for lines added later. Changing it never re-rates the order's existing lines, whether or not the same request sends sales_order_lines. To change an existing line's tax, send that line with its own tax_rate_id.
  • A line sent with tax_rate_id: null (and no tax_allocation of its own) has its tax rate removed and its tax cleared to 0. A line sent without a rate but with its own tax_allocation keeps that amount.
  • A line's tax is computed on its discounted amount, not its gross: (quantity × unit price − the line's pre-tax discount) × rate, rounded to 2 decimals. On a tax-inclusive order (is_tax_included: true) the tax is the part of that discounted amount that is tax.

Picking a saved address by id: send shipping_address: {"id": 812} (or billing_address) with no other address fields to attach one of the customer's saved addresses as-is. The id must be one of the address-book addresses of the customer the order will have after this update (the customer_id in the same request, or the order's current customer). It is refused with 422 on shipping_address.id / billing_address.id when the address does not exist, when it belongs to someone else, or when the order has no customer. An address object that carries address fields (with or without an id) is saved by value as before.

Path parameters:

  • salesOrder (required): Sales order ID.

Requires permission: sales_orders.update

Request​

Responses​

OK

Response Headers
    Content-Type