Guide
Orders and fulfilment
The order lifecycle: statuses, editing line items, backorders, cancellation requests, forwarding and book-back. What each state actually means and which transitions are safe to automate.
Orders carry more state than a status field suggests. This guide covers what the states mean, how line items change under you, and which operations are safe to run without a human watching.
Statuses
open · pending · on-hold · completed · cancelled
Filter with ?status=open, or pass several comma separated. An order arriving from a marketplace can land in any of them: a bol.com import can arrive already completed, which is why orders.completed fires on arrival as well as on transition.
Reading an order
GET /orders
gives you a page of summaries. GET /orders/get-single
takes either order_pk or order_number, and returns the full record with line items, addresses and channel.
Two identifiers appear throughout the order endpoints and they are not interchangeable:
order_pkis Stockpilot’s internal primary key. Most endpoints want this.order_numberis the human or marketplace facing reference.
The path parameter is sometimes named order_id and sometimes order_pk for the same value. Read the parameter name on each endpoint rather than assuming.
Editing line items
An order is not frozen once created. Four operations change its contents:
| Operation | Endpoint |
|---|---|
| Add a product | POST /orders/{order_pk}/items/add |
| Replace the product on a line, keeping quantity and price | POST /orders/{order_pk}/items/swap |
| Change quantity or record a refund | PATCH /orders/ordered-items/{item_id}/update |
| Remove a line | DELETE /orders/{order_id}/items/{item_id} |
items/add is not idempotent. A retry adds a second line rather than reconciling with the first. If the call fails ambiguously, re-read the order and check before sending it again. See rate limits and idempotency.
Deleting a line takes book_back, which decides whether the quantity returns to sellable stock. Omitting it is a decision, not a default to ignore: the units either come back into inventory or they do not.
Backorders
When stock will not cover a line, move it to backorder rather than leaving the order stuck:
POST /orders/{order_id}/move-to-backorder{
"backorder_items": [
{ "item_id": 123, "quantity": 5 },
{ "item_id": 456, "quantity": 2 }
]
}
The response reports what actually moved, per line:
{
"message": "Successfully moved 2 item(s) to backorder",
"items_processed": 2,
"backorder_status": "partial",
"backorder_results": [
{ "item_id": 123, "quantity_moved": 5, "new_backorder_qty": 5 }
]
}
Note backorder_status: "partial". Read the per line results rather than assuming a 200 moved everything you asked for.
Release them with POST /orders/{order_id}/move-from-backorder once stock lands. Neither direction is idempotent, because both apply a delta to a running backorder quantity.
Cancellations
There are two different things here, and conflating them is a common bug.
- A cancellation request is a marketplace or customer asking. GET /orders/cancellation-requests lists orders with an open request; GET /orders/cancellation-request checks one order.
- A cancellation is you acting. PUT /orders/cancel-order cancels the order.
A request does not cancel anything on its own. If you are building a queue for warehouse staff, poll the requests endpoint and let a human decide, especially for orders already picked.
Deleting an order
DELETE /orders/{order_id}
removes the order entirely, and takes book_back to decide whether every line’s quantity returns to inventory.
Deletion is not cancellation. A cancelled order stays in the record with a cancelled status and remains visible in reporting. A deleted order is gone. Reach for cancel-order unless you specifically need the record removed.
Forwarding
If another system fulfils an order, mark it forwarded so Stockpilot stops pulling it from the marketplace again:
PATCH /orders/{order_id}/update-forwarding{ "register_order_id": "MP-12345", "source": "api" }
The returned id is prefixed:
{
"forwarded_order_id": "FWD-MP-12345",
"message": "Forwarding details updated.",
"to_forwarded_channel": "api"
}
That FWD- prefix is what prevents duplicate fetching, so store the returned value rather than reconstructing it yourself. Channels also carry a channel_is_forwarded flag, covered in sales channels.
Fulfilment
POST /orders/fulfil records that an order shipped: carrier, tracking code, tracking URL, shipping method and timestamp. It also accepts a list of items, so partial fulfilment is a matter of naming the lines and quantities that actually went out.
Confirm with GET /orders/fulfillment , which returns the tracking details as stored.
Fulfilment and label buying are separate steps. Getting a label does not fulfil the order, and fulfilling does not buy a label. Shipping and labels covers the ordering, and Order to label walks the whole path.
Updating status directly
PATCH /orders/{order_id}/update-status sets the status field. It is an absolute write, so it is safe to retry.
Use it to reflect a decision you have already made, not to drive the pipeline. Setting an order to completed does not fulfil it, ship it, or move any stock.
