Delivery Statuses

The canonical delivery lifecycle, the precedence rules, and how four live providers map onto it.

Delivery Statuses

Every provider's own status vocabulary is mapped onto one canonical set. These ten values are the only ones that appear anywhere in YUMBI Gateway, and the only ones you may send.

StatusRankMeaning
pending0Delivery accepted, no driver assigned yet
assigned1A driver has the job — including travelling to the store and waiting there
left_store2Driver has collected the order and left
on_route3Driver is travelling to the customer
arriving4Driver is close to the dropoff
arrived5Driver is at the dropoff
delivered6Handed over — terminal
cancelled7Called off — terminal
failed7Could not be completed — terminal
undeliverable7Customer unreachable, address unusable, order returned — terminal

Precedence

The rank column is not decorative. An update ranking lower than the stored status is discarded, so out-of-order webhooks cannot rewind a customer's tracking screen. The update is still accepted with a 200; it simply changes nothing.

The single exception is a changed provider_delivery_id, which YUMBI Gateway reads as a re-dispatch and allows to reset the status downward.

Two consequences worth designing around:

You may skip statuses freely. Going straight from left_store to arrived is allowed — it is a forward move. Many providers have no en-route event and never send on_route at all.

You cannot un-arrive. If your platform models proximity as a flag that toggles on and off — a "courier is close" boolean, say — derive a status from it going true, never from it going false. Deriving on_route when the flag clears is a regression; YUMBI Gateway discards it, which is the protection working, but the cleaner implementation is not to send it at all.

assigned covers the whole pre-pickup leg

The most common mapping mistake. Everything before the food is in the driver's hands is assigned:

  • Driver allocated to the job → assigned
  • Driver travelling to the storeassigned
  • Driver waiting at the store → assigned

left_store, on_route, arriving and arrived are all after pickup. Mapping a to-the-store status onto on_route tells the customer a driver is on the way with food that has not been collected, and once sent it cannot be walked back.

If your platform models pickup and dropoff as separate legs with the same status vocabulary, the leg is what disambiguates. A "started" on the pickup leg is assigned; a "started" on the dropoff leg is left_store.

Terminal statuses

delivered, cancelled, failed and undeliverable end the delivery. Nothing moves it afterwards, polling stops, and the channel closes out the order.

Choose between them honestly:

UseWhen
deliveredThe customer has the food
cancelledCalled off before completion, by anyone
failedYour platform could not complete it — no driver found, a pricing or system failure
undeliverableThe delivery was attempted and could not be handed over — customer unreachable, address wrong, order returned to the store

failed and undeliverable share a rank but mean different things to the brand: failed is generally a provider problem, undeliverable a customer or address problem. Getting them right is what lets a brand tell whether it has a courier problem or a data problem.

Mapping patterns

Provider vocabularies vary more than the canonical set suggests. These are the shapes that come up, and how each resolves.

A near 1:1 vocabulary

Some platforms already model the lifecycle at the same granularity, and the mapping is close to a rename. Even then, expect one or two statuses that carry no lifecycle meaning — an "unknown", or a "not being tracked" marker. Map those to nothing rather than forcing them into pending.

Fewer states than the canonical set

A platform that signals only discrete milestones will have nothing to map onto on_route or arriving. That is fine — skipping forward is allowed, and a lifecycle of pendingassignedleft_storearriveddelivered is perfectly valid. Do not manufacture an intermediate status you cannot actually observe.

Watch the pre-pickup states in particular: a platform may have several (courier allocated, courier searching, courier at store) that all collapse to assigned.

More states than the canonical set

Where several of your statuses map to the same canonical one, send the mapped value each time and let idempotency absorb the repeats. An update that changes nothing is dropped without re-notifying the channel, so you do not need to track which ones you have already sent.

A flag rather than a state

Some platforms express proximity as a boolean alongside the status — "courier imminent" — rather than as a distinct state. Two rules:

  • Only derive a status from it on the dropoff leg. The same flag on the pickup leg means the driver is close to the store, which is still assigned.
  • Only derive from it going true. See "You cannot un-arrive" above.

Two legs sharing one vocabulary

A platform that models a delivery as a pickup job plus a dropoff job will push the same status values twice, meaning different things each time. The leg is what disambiguates:

Driver actionLegCanonical
Start — travelling to the storepickupassigned
Arrive — at the storepickupno change
Complete — food collectedpickupleft_store
Start — travelling to the customerdropoffleft_store (duplicate, dropped)
Arrive — at the customerdropoffarrived
Complete — handed overdropoffdelivered

The overlap in the middle produces a genuine duplicate. Send it anyway — YUMBI Gateway drops it.

Distinct failure modes

If your platform distinguishes why a delivery ended badly, keep that distinction rather than collapsing everything to cancelled. A courier that could never be found is failed; an order carried out and brought back is undeliverable. Return-to-store states — returning, returned, return confirmed — all belong under undeliverable.

Unmapped statuses

If your platform has a status with no canonical equivalent — a driver-at-store event, an internal state change — send nothing rather than forcing it into the nearest value. Nearly every provider ends up with at least one status mapped to "no change", and that is the right answer. An update with no delivery_status is still useful if it carries a driver name, ETA or position.


Did this page help you?