Skip to content

Delivery lifecycle

The order lifecycle has two related views:

  • the order state returned by the Orders API; and
  • normalized webhook events sent as fulfillment progresses.

Persist the First Delivery order.id returned by order creation. It is the stable correlation key across API reads, webhook payloads, provider attempts, and support investigations.

Simplified First Delivery order lifecycle

ready_now is an event layered on the current lifecycle state. It does not replace status_v3. Refunded is also an event rather than an order lifecycle state.

Lifecycle meaning Typical API status Webhook status_v3 Legacy webhook status
Scheduled for later scheduled scheduled Pending
Waiting for assignment pending_assign — Pending
Waiting for merchant acceptance pending_merchant — Pending
Courier assigned / travelling to pickup en_route_pickup en_route_pickup Assigned
Courier at pickup arrived_pickup arrived_pickup AtPickup
Picked up / travelling to customer en_route_dropoff en_route_dropoff InTransit
Courier at customer arrived_dropoff arrived_dropoff AtDestination
Delivered completed completed Delivered
Cancelled cancelled cancelled Cancelled
Provider changed after another attempt commonly pending_assign until courier assignment reassign Reassign

Not every provider emits every intermediate event. Driver, GPS, ETA, proof-of-delivery, and provider tracking fields are conditional.

The public Orders API normalizes several internal queue states: internal pending_dispatch, pending_reassign, and staging are exposed as pending_assign; internal pending_vendor becomes pending_merchant; and internal queued becomes scheduled. Raw internal state can appear separately as _status.

Internal pending_reassign does not always mean a second provider is being tried. It is also used after an external delivery is created while First waits for that provider to assign a courier. The reassign webhook event is narrower: it signals that the external provider delivery changed.

  1. Save order.id, your external_id, and _request_id.
  2. Acknowledge webhook requests quickly and process them asynchronously.
  3. Make webhook processing idempotent.
  4. Never assume webhook ordering or completeness.
  5. Reconcile critical workflows by reading the order after a webhook, timeout, or suspected gap.
  6. Treat completed and cancelled as terminal unless a documented reassign begins a new provider lifecycle.

Use the published cancel endpoint with the First Delivery order ID. Do not simulate cancellation by locally changing status.

Cancellation can be rejected after fulfillment has progressed too far. Provider-specific cutoffs differ, so persist the response and refetch the order before deciding whether to retry or escalate.

Reassignment can occur after a provider release, a routing retry, or an enabled auto-reassign threshold. During a reassignment race, more than one provider attempt can temporarily exist for the same order. The first attempt that obtains a courier wins; First cancels the remaining active attempts.

Your integration should correlate by orderId, accept a later reassign as a new fulfillment lifecycle, and avoid treating repeated statuses as separate customer orders.

See Auto-routing strategies for provider selection and fallback behavior.