A trip moves through a defined set of statuses from the moment you book it to the moment it is delivered, canceled, or expired. This page lists every status, what triggers each change, which states are terminal, and the full set of webhook events the Courier API emits. The status field on a trip always holds its current state.
Statuses
Each status change fires the matching webhook event. Subscribe to the events you need when configuring a webhook in the Partner Portal; each event’s data object holds the resource the event is about. The envelope and signature verification are covered in the platform Webhooks guide.
| Status | Meaning | Webhook event |
|---|---|---|
pending |
Trip created, waiting for a driver to be assigned | courier.trip.created |
assigned |
A driver is available and on the way to pickup; driver details are now available | courier.trip.assigned |
at_pickup |
The driver has arrived at the pickup location | courier.trip.at_pickup |
started |
Pickup is done and the driver is delivering | courier.trip.started |
completed |
All dropoffs delivered; the trip is finished | courier.trip.completed |
canceled |
The trip was canceled by you, the driver, or the system | courier.trip.canceled |
expired |
No driver was assigned within the acceptance window | courier.trip.expired |
The usual order
A delivery that goes smoothly progresses from pending to completed. From any active state before delivery it can also be canceled, and a trip that is never accepted expires.
completed, canceledor expired does not change again.Multiple dropoffs
A trip can have more than one dropoff. While the trip is started, the driver works through the dropoffs in order, and each stop emits its own events as it begins and finishes:
courier.trip.stop.started → driver is heading to a dropoff
courier.trip.stop.completed → that dropoff was delivered
The trip stays started across all the intermediate stops and moves to completed only once the final dropoff is delivered. These stop events fire once per dropoff, including on a single-dropoff trip, where you see exactly one pair between started and completed. If you only handle single-dropoff deliveries, you can rely on started and completed alone and treat the stop events as optional detail.
Note: The stop events always fire, even for a single dropoff, because they carry per-destination delivery detail, such as which dropoff was served and its delivery confirmation. Firing them on every trip means you handle one and many dropoffs with the same code path, and gives you a single, consistent place to capture proof of delivery per destination.
Terminal and cancelable states
completed, canceled, and expired are terminal. A trip in any of these states does not change again, and trying to cancel one is rejected with a 409 Conflict.
A trip can be canceled while it is still pending, assigned, or at_pickup, using POST /courier/v1/trips/:trip_id/cancellation. A trip that is never accepted moves to expired on its own.
Following the lifecycle
In production, the reliable way to follow these transitions is webhooks: subscribe to the Courier trip events and react as each fires. You can also read the current state at any time with GET /courier/v1/trips/:trip_id. For the delivery mechanism and signature verification, see the platform Webhooks guide.
Webhook events
The status-transition events are listed in the Statuses table above (courier.trip.created through courier.trip.expired). In addition, the Courier API emits the per-dropoff, messaging, and payment events below.
Per-dropoff events
courier.trip.stop.started — The driver is heading to one of the trip’s dropoffs. Fires once per dropoff, including on a single-dropoff trip.
courier.trip.stop.completed — A dropoff was delivered. Fires once per dropoff as each delivery is completed, including on a single-dropoff trip.
Messaging events
courier.trip.chat.message — A new message was sent in the trip chat between the client and the driver. Use it to sync conversation activity or trigger a notification.
Payment events
courier.trip.payment.completed — Payment for the trip was processed successfully.
courier.trip.payment.refunded — A refund was issued against the trip’s payment.
In sandbox
Sandbox has no live drivers, so a booked trip stays at pending and none of these transitions happen on their own. To move a sandbox trip through its lifecycle and generate the matching events, use the sandbox testing tools provided with your sandbox access — see Testing the full lifecycle in sandbox. They are the only way to reproduce driver activity in sandbox.