Statuses and reasons
Three fields tell your system what happened to an order:
| Field | Answers | Example |
|---|---|---|
status | Where is the order in its life? | COMPLETED |
task_status | What physically happened last? | DELIVERY_COMPLETED |
status_reason | Why did it end that way? | completed.alternative_recipient |
With status_description (free text that goes with a reason) and actual_amounts (what was
really handed over, when it differs from amounts). All five are on the order and in every
status update.
Statuses
status | Means | Set by |
|---|---|---|
CREATED | Exists, not yet committed to a delivery | You, by creating with "status": "CREATED" |
RESERVED | Committed to be delivered, waiting to be planned | Creating an order (the default), or you |
ROUTED | On a route | Saving a route: in the dashboard, by automatic planning, or through the Routes API |
ONGOING | Being worked: one task is done, another is still open — for example picked up at the shop, not yet delivered | The driver app |
COMPLETED | Every task reached an outcome | The driver app |
FAILED | The last attempt did not succeed | The driver app |
CANCELED | Will not be delivered | You or a dispatcher |
A shop-to-recipient order normally moves RESERVED → ROUTED → ONGOING → COMPLETED.
COMPLETED and CANCELED are finalA failed delivery goes back to be planned again, so the same order can be FAILED today and
COMPLETED tomorrow. Do not close the order in your system on FAILED, and do not discard a
second update for the same order as a duplicate.
task_status is <TASK>_<OUTCOME>, for example PICKUP_COMPLETED, DELIVERY_COMPLETED or
DELIVERY_FAILED, and null until a task has an outcome. For "has the recipient got it?", check
task_status = DELIVERY_COMPLETED.
The full rules are on Order lifecycle.
Reasons
status_reason is a code from a closed list, namespaced by the status it explains:
completed.* only with COMPLETED, failed.* only with FAILED, canceled.* only with
CANCELED. Every code and its meaning is listed on
Outcome reasons.
nullmeans no reason was recorded. It is not the same ascompleted.clean, which says someone checked and there was nothing to note.*.otheralways comes with astatus_description.status_descriptionmay also accompany any other code, as extra detail for people.- New codes may be added. Map by family first (
completed.*,failed.*,canceled.*) so an unknown code still lands in the right bucket.
| What | Status |
|---|---|
| Reasons stored on orders and returned by the API | Live |
Setting canceled.* when you cancel through the API | Live |
| Setting a reason when a stop is completed or failed through the Routes API | Live |
| Drivers choosing a reason in the driver app | Coming. Until then, driver completions arrive with status_reason: null. |
Migrating from the legacy platform
If your system received driver deviations from the legacy platform, map them like this:
| Legacy deviation | status | status_reason | Notes |
|---|---|---|---|
ok | COMPLETED | completed.clean | |
neighbour / neighbor | COMPLETED | completed.alternative_recipient | |
door | COMPLETED | completed.left_unattended | |
funeral | COMPLETED | completed.alternative_recipient | status_description says where, for example "Delivered to the funeral home" |
return_to_store | FAILED | failed.returned_to_origin | Taken back to the shop; may be planned again |
other | FAILED | failed.other | status_description holds the driver's text |
Two differences from legacy:
- There is no
completed.other. A delivery that happened with an unusual circumstance gets the closestcompleted.*code, with the details instatus_description.othermaps tofailed.otherwhen the goods were not delivered, orcanceled.otherwhen the order was called off. - No reason is not the same as
ok. Legacy could not tell them apart; herenullandcompleted.cleanare different answers.
Your own status codes
The codes above are the same for every partner. Translating them into your own ERP status codes
is done on your side: keep a table from status + status_reason to your code, and fall back
per family for codes you have not mapped. If you need Zoopit to send your own codes instead,
per-partner mapping is available on request — contact us.