Shipment Lifecycle
Every shipment moves through a state machine. The same statuses cover both same-city and regional deliveries — only the path differs.
State machine
PENDING
│ (transient — only seen if you use gateway payment instead of wallet)
▼
CONFIRMED payment cleared, dispatch starts
│
▼
PICKUP_DISPATCHED first-mile rider assigned, heading to sender
│
▼
PICKED_UP rider has the package
│
├── (same-city) ──────────┐
│ │
▼ (regional) │
IN_TRANSIT │ hub-to-hub transit via bus partner
│ │
▼ ▼
OUT_FOR_DELIVERY last-mile rider heading to receiver
│
▼
DELIVERED ✅
Terminal states: DELIVERED, CANCELLED, FAILED.
Status reference
| Status | Meaning | What happens |
|---|---|---|
pending | Just created, awaiting payment | For wallet shipments, this exists only for a fraction of a second before flipping to confirmed |
confirmed | Payment received | Auto-dispatch begins — engine picks the nearest available rider |
pickup_dispatched | Rider assigned | Rider has the job in their app and is heading to the sender |
picked_up | Rider has the package | Sender's part is done |
in_transit | Regional only — hub-to-hub | Package is on a bus between cities |
out_for_delivery | Last-mile rider heading to receiver | Final leg |
delivered | Successfully delivered | Terminal — proof-of-delivery captured |
cancelled | Cancelled before pickup | Wallet refunded |
failed | Delivery attempts exhausted | Returned or held for resolution |
Same-city vs regional
| Status | Same-city | Regional |
|---|---|---|
pending | ✓ | ✓ |
confirmed | ✓ | ✓ |
pickup_dispatched | ✓ | ✓ |
picked_up | ✓ | ✓ |
in_transit | — (skipped) | ✓ |
out_for_delivery | ✓ | ✓ |
delivered | ✓ | ✓ |
Same-city shipments go directly from picked_up → out_for_delivery. Regional shipments pass through in_transit while the package is on a bus between sorting hubs.
Journey legs
Each shipment has 1 or 3 legs — segments handled by different actors:
| Leg | Direction | Handled by | Used in |
|---|---|---|---|
first_mile | Sender → origin hub (or full route for same-city) | Rider | Both |
transit | Origin hub → destination hub | Bus partner | Regional only |
last_mile | Destination hub → receiver | Rider | Regional only |
Legs appear in the legs array of every shipment response. Each leg has its own status (pending, in_progress, completed, failed) so you can show progress per segment in your UI.
Hub sub-events (regional)
Regional shipments pass through sorting hubs. These don't change the top-level status but are recorded in the events array:
| Event description | When |
|---|---|
Shipment received at {hub_name} | Package checked in at origin hub |
Package containerized for transit | Loaded onto an outgoing container |
Container dispatched via {partner} | Bus departed |
Container arrived at {hub_name} | Bus arrived at destination hub |
Last-mile rider assigned | Local rider picked up from destination hub |
Use the events array on GET /merchant/shipments/{id} to surface fine-grained progress.
Webhook mapping
Each status transition fires a corresponding webhook. See Webhooks for payloads.
| Transition | Event |
|---|---|
→ confirmed | shipment.created, shipment.paid |
→ picked_up | delivery.picked_up |
→ delivered | delivery.delivered |
→ cancelled | shipment.cancelled |
→ failed | delivery.failed |
Cancellation rules
Cancellable from: pending, confirmed, pickup_dispatched.
Not cancellable from: picked_up, in_transit, out_for_delivery, delivered, cancelled, failed.
Once the rider has the package, contact support for resolution.