Skip to main content

Statuses and reasons

Three fields tell your system what happened to an order:

FieldAnswersExample
statusWhere is the order in its life?COMPLETED
task_statusWhat physically happened last?DELIVERY_COMPLETED
status_reasonWhy 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​

statusMeansSet by
CREATEDExists, not yet committed to a deliveryYou, by creating with "status": "CREATED"
RESERVEDCommitted to be delivered, waiting to be plannedCreating an order (the default), or you
ROUTEDOn a routeSaving a route: in the dashboard, by automatic planning, or through the Routes API
ONGOINGBeing worked: one task is done, another is still open — for example picked up at the shop, not yet deliveredThe driver app
COMPLETEDEvery task reached an outcomeThe driver app
FAILEDThe last attempt did not succeedThe driver app
CANCELEDWill not be deliveredYou or a dispatcher

A shop-to-recipient order normally moves RESERVED → ROUTED → ONGOING → COMPLETED.

Only COMPLETED and CANCELED are final

A 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.

  • null means no reason was recorded. It is not the same as completed.clean, which says someone checked and there was nothing to note.
  • *.other always comes with a status_description.
  • status_description may 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.
WhatStatus
Reasons stored on orders and returned by the APILive
Setting canceled.* when you cancel through the APILive
Setting a reason when a stop is completed or failed through the Routes APILive
Drivers choosing a reason in the driver appComing. 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 deviationstatusstatus_reasonNotes
okCOMPLETEDcompleted.clean
neighbour / neighborCOMPLETEDcompleted.alternative_recipient
doorCOMPLETEDcompleted.left_unattended
funeralCOMPLETEDcompleted.alternative_recipientstatus_description says where, for example "Delivered to the funeral home"
return_to_storeFAILEDfailed.returned_to_originTaken back to the shop; may be planned again
otherFAILEDfailed.otherstatus_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 closest completed.* code, with the details in status_description. other maps to failed.other when the goods were not delivered, or canceled.other when the order was called off.
  • No reason is not the same as ok. Legacy could not tell them apart; here null and completed.clean are 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.