How sales order status works
A sales order in SKU.io doesn't have a single "status." It tracks three separate status axes at once — order status, fulfillment status, and payment status — each answering a different question and each moving on its own schedule. Understanding the three axes is the key to reading an order at a glance, and to knowing which values are yours to set versus which SKU.io calculates for you.
Words like allocation, backorder, fulfillment order, and shipment are defined the first time they appear below and collected in the glossary.
The three status axes
Open any sales order, such as SO-DOCS-0001 (customer Blue Bottle Retail, store Main Store, warehouse Main Warehouse), and the header shows these three indicators side by side. Each one is tracked, filtered, and displayed on its own.
| Axis | Answers | Who sets it | Values |
|---|---|---|---|
| Order status | Where's this order in its lifecycle? | You (approve, close, cancel, revert) — with two automatic assists | Draft, Reserved, Open, Closed (Cancelled) |
| Fulfillment status | How much has shipped? | Calculated from lines, shipments, and fulfillment orders | Unfulfilled, Partially Fulfilled, Awaiting Tracking, Awaiting Pickup, Fulfilled, Over-fulfilled, Out of Sync |
| Payment status | How much has been paid? | Calculated from payments and refunds | Unpaid, Partially Paid, Paid, Partially Refunded, Refunded |
Because the three axes are independent, a single order can hold a different value in each at the same time. That's normal — read the header as a composite picture, not one on/off switch. An order can be Open (still active) while its fulfillment reads Awaiting Tracking (handed to a carrier, no tracking back yet) and its payment reads Paid. For the full meaning of every value across all axes, see the status reference.
The three axes aren't entirely isolated, though. They meet at two edges: when an order becomes fully shipped, the fulfillment recompute closes the order for you, and when an order is fully refunded, the payment recompute also closes it. Those two couplings are called out in their sections below.
Order status: The lifecycle you drive
Order status is the primary lifecycle. It's the axis you control directly through approve, close, cancel, and revert actions — with two automatic assists from the fulfillment and payment axes.
| Order status | What it means |
|---|---|
| Draft | The default for a new order. It's fully editable, holds no stock, and takes no part in the allocation and fulfillment order pipeline. A shipping method isn't required while an order is a draft. |
| Reserved | Approved with stock committed to its lines through allocations, but deliberately held back from shipping (for example by an open delay, a hold-until date, or an on-hold flag). A reserved order participates in the fulfillment pipeline, and can carry a scheduled auto-open — see the note below. |
| Open | Approved and active. Stock is committed and the order is ready to fulfill, invoice, and take payment against. |
| Closed | The lifecycle is finished — the order shipped in full, or you closed it by hand. |
| Cancelled | A cancelled order is surfaced separately in the UI, but under the hood it's a closed order with a cancellation timestamp set. See the note below. |
You move an order from Draft into the pipeline by approving it: a normal approval lands it at Open, and approving with the reserve option lands it at Reserved instead. From there, fulfilling to completion or closing by hand moves it to Closed, and cancelling closes it with side effects. See Approve, reserve, and allocate stock and Close, cancel, archive, and delete orders.
Only the order-status axis is shown here. Fulfillment and payment status advance on their own and are covered below.
A few behaviors are worth knowing before they surprise you.
Allocation, fulfillment orders, and shipping all operate on Open and Reserved orders. A Draft hasn't been opened yet, and a Closed or Cancelled order's fulfillment records are torn down by their own lifecycle — so neither takes part.
Reserving an order doesn't have to be permanent. An order can carry a hold-until date — a "don't ship before" date recorded on the order — and while that date is still in the future the order counts as held: it stays Reserved and is kept out of fulfillment. On a scheduled check, once the hold has elapsed and the order is eligible, SKU.io opens it to Open on its own so it can ship — you don't have to unreserve it by hand.
For channel orders, SKU.io also surfaces why an order is held and when it's expected to open. The hold reason is one of an open delay (a configured wait after the order is placed), a hold-until date, or an indefinite on-hold flag — and every hold except an indefinite on-hold shows its scheduled open time. To hold an order or set its release date yourself, see Approve, reserve, and allocate stock.
When you cancel an order, SKU.io stamps a cancellation timestamp and stores the order status as Closed. The UI reads that timestamp to show it as Cancelled. Cancelling also deletes non-shipped shipments, cancels active allocations, marks the remaining quantity on each line as cancelled, removes the accounting transaction, and queues a recall of any live provider fulfillment. See Close, cancel, archive, and delete orders.
Approval is refused on a Closed order. To keep working an order that has closed, the path back depends on why it closed — reverting to draft reverses fulfillment and reopens editing (blocked while a shipment is awaiting tracking or the order is closed), and voiding a shipment on a fully fulfilled order reopens it automatically when it drops below fully shipped.
Closing by hand is blocked if any product line still has an active backorder (stock committed but not yet on hand), or if any line hasn't been mapped to a product. Resolve those first. See Inventory allocation and backorders and Manage order lines.
Fulfillment status: Calculated from what ships
Fulfillment status is calculated for you. SKU.io recomputes it every time a fulfillment line is written or deleted — no matter which path made the change (manual ship, bulk fulfill, channel sync, or a health repair). You never type this field in; you record a shipment and the status follows.
The recompute walks a priority ladder and stops at the first rule that matches. This ordering is deliberate: the states that carry the strongest signal win.
| Priority | Fulfillment status | Reached when | Side effect on order status |
|---|---|---|---|
| 1 | Over-fulfilled | A line shipped beyond the quantity the order can absorb (an inventory-integrity signal). | Closes the order, stamps the fulfilled date, and fires the shipped event (same side effects as Fulfilled) |
| 2 | Fulfilled | Every line is shipped in full. | Closes the order, stamps the fulfilled date, and fires the shipped event |
| 3 | Awaiting Pickup | A fulfillment order is staged for in-person collection (Click & Collect). No shipment exists yet — the state lives on the fulfillment order. | Sets the order to Open |
| 4 | Awaiting Tracking | Work has been dispatched to a carrier, 3PL, or dropship supplier, but no tracking has come back yet. | Sets the order to Open |
| 5 | Partially Fulfilled | Some, but not all, line quantity has shipped. | Sets the order to Open |
| 6 | Unfulfilled | Nothing is in flight. | Keeps Reserved (if held), otherwise Open |
Because the ladder sets order status as a side effect, the two axes stay consistent: reaching Fulfilled or Over-fulfilled is exactly how an Open order becomes Closed on its own, and voiding a shipment recomputes the ladder downward — pulling a closed order back to Open. This is also why an order can legitimately read Open · Awaiting Tracking · Paid: the order is active, the shipment is with the carrier, and the money is in.
Out of Sync isn't produced by the recompute above. It appears when SKU.io's shipments were removed but the sales channel still shows the order as fulfilled, leaving the two systems disagreeing. It clears only through the reset action — see Void, restore, or reset a shipment.
Orders that Amazon fulfills (AFN) ship entirely outside SKU.io and carry no local shipment records, so the ladder would wrongly read them as unfulfilled. Instead, a non-cancelled FBA order is pinned to Fulfilled and Closed regardless of the record count. A cancelled FBA order falls through and closes normally. See Amazon FBA, MCF, and externally fulfilled orders.
A few sales channels (Temu, for example) don't expose per-line shipment data, so SKU.io's line math can't tell "nothing shipped" apart from "everything shipped." When the channel itself reports partial fulfillment, that signal is trusted and the status is pinned to Partially Fulfilled — the one exception being an over-fulfillment, which SKU.io always surfaces because the channel has no concept of it. See How channel orders sync.
The automatic recompute rides on Eloquent model events, which don't fire on bulk query-builder updates or inserts. A path that writes fulfillment lines in bulk must trigger the recompute explicitly, or the header status can drift stale until the next line change.
For the full list of shipment-level states behind these order-level values, see Two-tier fulfillment: intent vs execution.
Payment status: Calculated from payments and refunds
Payment status is also calculated for you, from the payments and refunds recorded against the order. It's independent of where the order sits in its lifecycle — an unshipped order can be Paid, and a fully shipped order can be Unpaid.
| Payment status | What it means |
|---|---|
| Unpaid | No payment has been captured. |
| Partially Paid | Some payment is captured, but it doesn't yet cover the order total (net of any credits). |
| Paid | Captured payments cover the order total. Reaching this stamps the fully paid date. |
| Partially Refunded | A refund has been recorded, but it doesn't fully offset what was paid. |
| Refunded | Refunds fully offset every payment. This uniquely also closes the order (see below). |
You change payment status by recording, editing, or deleting payments and refunds, or by applying store credit — SKU.io derives the status from the running totals. Guards apply: you can't take payment on an order that hasn't been approved, a payment can't exceed the remaining balance, and a refund can't exceed what has been paid. See Record payments and apply store credit.
Fully refunding an order is the one payment transition that reaches across axes: alongside setting payment status to Refunded, it forces order status to Closed. A partial refund doesn't.
Some editing guards treat a Paid order the way they treat a closed order, to protect an order whose money has already settled. If a field is locked on a paid order, that's why.
Worked example: One order, three axes over time
Follow SO-DOCS-0001 — three lines for Blue Bottle Retail — and watch each axis move on its own:
| Moment | Order | Fulfillment | Payment |
|---|---|---|---|
| You create the draft | Draft | Unfulfilled | Unpaid |
| You approve it | Open | Unfulfilled | Unpaid |
| The customer pays in full | Open | Unfulfilled | Paid |
| You dispatch the shipment to the carrier | Open | Awaiting Tracking | Paid |
| Tracking comes back and every line ships | Closed | Fulfilled | Paid |
| Later, you refund the whole order | Closed | Fulfilled | Refunded |
Notice how at the fourth row the order reads Open · Awaiting Tracking · Paid — three true, different facts about one order. The order status flips to Closed on its own only when fulfillment reaches Fulfilled, and the final full refund is what moves payment to Refunded (and keeps the order closed).
Next steps
- Sales order status reference — every value across all axes
- Two-tier fulfillment: intent vs execution — the shipment states behind the fulfillment axis
- Approve, reserve, and allocate stock
- Fulfill a sales order
- Close, cancel, archive, and delete orders
- Record payments and apply store credit
- Sales orders overview