Order lifecycle

Updated September 9, 20265 min read

This page explains how an Order progresses from ingestion to closure. It also shows how Order status rolls up from fulfillment orders and Shipments. All three lifecycles are distinct but connected; knowing how they relate is the foundation of order management.

Status flow

An Order's status is computed from the statuses of its Fulfillment orders. Fulfillment order status is in turn computed from the statuses of its line items. The progression looks like:

open → partially_allocated → allocated → processing → fulfilled → closed
                                                                 ↘ cancelled (alternative terminal)
StatusMeaning
openNo fulfillment orders yet, or all FOs are still open.
partially_allocatedSome FOs are allocated; others are still open.
allocatedAll FOs reached at least allocated.
processingAt least one FO is in pick / pack / fulfillment.
fulfilledAll FOs are fulfilled (or in a terminal state).
closedAll Shipments delivered → line items closed → FOs closed → Order closed.
cancelledEvery FO was cancelled.
on_holdThe order is held. This one is set, not computed. See Putting an order on hold.

For the full criteria, see Order status codes.

How the rollup works

The cascade from leaf to root:

  1. A Shipment reaches delivered.
  2. Every line item linked to that Shipment moves to closed.
  3. The fulfillment order moves through processingfulfilledclosed as its line items reach those states.
  4. The Order moves through allocatedprocessingfulfilledclosed as its FOs reach those states.

Cancellations propagate the same way: when every leaf is cancelled the parent flips to cancelled.

See Fulfillment order status codes and Line item status codes for the full criteria at each level.

Order status vs shipment status

A common point of confusion. They're related but answer different questions:

StatusQuestion it answers
Order status"How far through fulfillment is this customer purchase?"
Shipment status"Where is this specific parcel right now?"

An Order can be fulfilled while one of its Shipments is still in_transit. The Order moves to fulfilled once handover is complete; delivery (and the cascade to closed) can follow later.

Allocation timing

When does allocation happen? Two patterns:

  • At ingestion. Allocation rules fire as soon as the order lands. Fulfillment orders are created and allocated immediately. Best when inventory and rules are stable enough to commit early.
  • On-demand. The Order sits in open while ops or an upstream system decides allocation. Fulfillment orders are produced manually or via a deferred rule run.

Both are valid. Most integrations pick at-ingestion for speed; ops override (re-allocate) when needed.

Updating orders via bulk import

The bulk import endpoint (POST /orders/bulk/import) supports both creation and update. When a partner_order_reference already exists and the order is in open, partially_allocated, or allocated status, the import applies a partial merge — the same logic as the single-order update endpoint. Orders that have progressed beyond allocation (processing, fulfilled, closed, cancelled) are rejected from bulk updates and must be updated individually.

This makes the bulk import endpoint safe for periodic syncs from external systems: re-importing the same file updates existing orders in early stages without creating duplicates.

Putting an order on hold

on_hold sits outside the progression above. It is a pause, not a stage. An Order in open, partially_allocated, or allocated can be held. Every open or allocated fulfillment order on it then shows on_hold too.

While the hold is active, updating the Order is rejected, and so is every fulfillment order operation except cancel. Picking is blocked too. Cancelling a held Order is allowed, and it releases the hold on the way through.

on_hold_details records the hold: held_by, hold_date, the optional sla_hours you supply, and sla_timestamp. That timestamp is hold_date plus sla_hours. Releasing the hold leaves the same object in place with released_by, release_date, and how long the hold lasted. Hold the Order again and the previous hold moves into history, so the record accumulates rather than being overwritten.

A single fulfillment order can also be held on its own. The Order's status is unchanged in that case, and only that fulfillment order is blocked.

Cancellation semantics

Cancelling the whole Order. Cancellation is applied while the Order is open, partially_allocated, allocated, or on_hold. For an Order in processing, fulfilled, or closed, the request still succeeds. You get a 200, the Order comes back unchanged, and the webhooks fire anyway. Nothing in the response says the request was ignored, so check status in the response rather than the HTTP code. Past that point, cancel at the fulfillment order or line-item level instead. Cancelling an Order that is already cancelled is the one case that is rejected outright.

Cancelling a fulfillment order that has already been fulfilled. A fulfilled FO must be unfulfilled before it can be cancelled. Unfulfilling reverses the fulfillment state and cascades back to Shipments. Any Shipment whose items all become unfulfilled is automatically cancelled, provided the Shipment is still in a cancellable state.

Partial cancellation. Cancelling a single line item or Fulfillment order while leaving the others in place is supported. The Order continues toward fulfilled / closed based on the lines that did fulfil.

Audit and diagnostics

Every order exposes one timeline: GET /orders/{reference}/activity. It returns a paginated history built from three sources:

  • Change logs. Field-level edits, diffed from the order snapshot taken after each write. They answer "what changed and when".
  • System logs. API calls and internal events, each with its timing and response code. They answer "what did the integrations actually exchange".
  • Notifications. Customer notifications sent against the order, carried as request_type NOTIFICATION.

A change log and the system log that caused it merge into a single entry. A system log with no change log appears on its own, without changes. Housekeeping fields that move on every write, such as update_date, are left out.

The timeline is the first diagnostic stop when an order looks wrong.

How it fits with other modules

  • Shipping. Order fulfillment produces shipments; shipment delivery cascades back into FO and Order status.
  • Inventory. Allocation decisions often depend on inventory visibility per location.
  • Returns. Return requests reference an order via partner_order_reference.