Skip to Content
Order lifecycle & cancel

Order lifecycle & cancellation

Order state is primarily unified_json.status (string, max 128 chars). Workers, rider apps, simulators, and PATCH /v1/orders/:id update this field. orders.cancelled_at is set when a cancel succeeds.


Typical statuses

Integrations usually observe:

StatusTypical meaning
pendingCreated / awaiting dispatch
acceptedRider accepted
picked_upPicked up at merchant
in_transitEn route to customer
deliveredCompleted
cancelledVoided

Additional strings may appear (in_transit_delayed, returned_to_merchant, lost_in_transit, etc.) — treat unknown values gracefully.


Merchant cancel — POST /v1/orders/:id/cancel

  • Auth: orders:write on ilv_… or merchant JWT (same billing gates as POST /v1/orders; keys:manage too when ORDER_POST_REQUIRES_KEYS_MANAGE=1).
  • Body: optional reason (defaults to a generic API string), optional notes.
  • 200 — order cancelled, or already_cancelled: true on replay.
  • 409 cannot_cancel — disallowed status (see below).
  • Fires order.status_updated outbound with previous_status and status: cancelled.

Policy (aligned with admin cancel)

  • Default: cancel allowed until terminal fulfilment: delivered, returned_to_merchant, lost_in_transit.
  • ADMIN_ORDER_CANCEL_PRE_PICKUP_ONLY=1: stricter — cancel blocked from picked_up, in_transit, terminal outcomes, etc. (same env as admin break-glass cancel).

Admin cancel — POST /v1/admin/orders/:orderId/cancel

Operator/support path with admin RBAC. Same merge to cancelled and audit order.admin_cancelled. Use when the merchant API cannot proceed (account locked, dispute, etc.).


Status changes without cancel

Merchants may PATCH /v1/orders/:id with status / payment_verified when permitted (commission and env gates apply). Rider flows and simulators advance statuses and emit webhooks (order.picked_up, order.delivered, etc.).


Next steps