Journey State Updates
Field Reference
All webhook payloads follow the same structure with type, data, and errors fields at the root level. The data object contains the following fields:
| Field | Type | Required | Description | Example Values |
|---|---|---|---|---|
client_id | string | Yes | Your Cabify client identifier | "512cc654f0e342efcdd3fdfde9fd8000" |
journey_id | string | Yes | Unique journey identifier | "3fad6e43719a48f3904b5f2d54865b1a" |
user_id | string | Yes | User who created the journey | "591bbf5d7db7d84023ca3760ba1f1000" |
rider_id | string or null | Yes | Passenger identifier. Will be null if the rider is external to Cabify (not a registered Cabify user) | "591bbf5d7db7d84023ca3760ba1f1000" or null |
state | string | Yes | Current journey state | "hire", "hired", "hot-hired", "not found", "arrived", "pick up", "stop changed", "finished", "terminated" |
event_date | string (ISO 8601) | Yes | When the event occurred | "2020-04-28T15:26:46.736462Z" |
created_at | string (ISO 8601) | Yes | When the journey was created | "2020-04-28T15:26:36Z" |
start_type | string | Yes | Journey start type | "asap", "reserved" |
start_at | string (ISO 8601) | Yes | Scheduled start time | "2020-04-28T15:26:36Z" |
driver_id | string | No | Assigned driver identifier. In a hire event a non-null value means this driver was just unassigned from the journey (see Hire Event). | "1d254ba6aff9ddd82ff5543ae85a88eb" or null |
preassigned_driver_id | string | No | Preassigned driver identifier. Populated in hire events when a driver is preassigned to a reservation; null otherwise (including all non-reservation events and once the preassigned driver is removed). | "1d254ba6aff9ddd82ff5543ae85a88eb" or null |
finished_at | string (ISO 8601) | No | When journey finished | "2020-04-28T15:32:01Z" or null |
terminated_at | string (ISO 8601) | No | When journey was terminated | "2020-04-28T16:00:00Z" or null |
finish_reason | string | No | Reason for journey completion | "drop off", "no show", "rider cancel", "driver cancel", "not found" |
loc | array | No | Driver's location when the state changed, as [lat, lng] | [40.4169, -3.7061] or null |
stop_id | string | No | Stop id that triggered the state change | "d3b05d77-646a-46e5-bfd0-cf69a370afb8" or null |
stop_contacts | array | No | An array of contacts in the stop | [{"name": "Carlos Jesús", "mobile_cc": "34", "mobile_num": "677777777", "stop_action": "pickup"}] or null |
is_driver_on_another_journey | boolean | No | true when the assigned driver is completing another trip before heading to pickup. Only present in hot-hired events. | true, false |
driver_stop_before_pickup | object or null | No | The driver's intermediate stop before reaching the pickup point. Contains loc as [lat, lng]. May be null even when is_driver_on_another_journey is true. Only present in hot-hired events. | {"loc": [40.42, -3.70]} or null |
| Notes: |
-
loc: Present for post-assignment events only (hired,hot-hired,arrived,pick up,finished). It reflects the driver's location at the moment the state transition occurred. It isnullwhen:- No driver has been assigned yet (
hire,not found) - For
finishedevents, location is only populated forfinish_reason: "drop off"andfinish_reason: "no show"; for other finish reasons, it isnull
- No driver has been assigned yet (
-
stop_idandstop_contacts: These fields are only populated for events that involve a specific stop (arrived,pick up). For other events (hire,hired,not found,finished,terminated), these fields will benullas they are not associated with a specific stop. We send notifications for everyarrivedandpick upevent (including every stop in multi-stop journeys); you can rely on receiving them for validation. Even forarrivedandpick upevents,stop_idmay benullif:-
The event coordinates don't match any stop within 150 meters
-
The event doesn't include valid coordinates
-
There's an error matching the stop to the journey
Important: The
stop_idin webhook events is resolved by matching the driver's GPS coordinates to the nearest journey stop within a 150-meter radius. This means thestop_idreturned in a webhook may differ from the stop identifiers you provided when creating the journey. To reliably correlate webhook events with your original stops, use thestop_contactsinformation (passenger name, phone number) or the event sequence order rather than relying solely onstop_idequality. -
-
stop_action: For multi-stop journeys, thestop_contactsarray includes astop_actionfield that indicates the action at each stop:"pickup"when a passenger is picked up, or"dropoff"when a passenger is dropped off. This field is only present inpick upevents. Forarrivedevents,stop_actionis not included instop_contactsas the action hasn't occurred yet. -
Last stop of a multi-stop journey (enriched stops): the final drop-off is delivered as its own
pick upevent carrying that stop'sstop_id/stop_contacts/stop_action— the same way intermediate stops are reported. It is not folded into thefinishedevent. Thefinishedandterminatedevents remain without stop information (stop_idandstop_contactsarenull). This applies to journeys that use per-stopcontacts(enriched stops); journeys without enriched stops are unaffected.Event Ordering for Drop-off Finishes: When a journey completes via drop-off on enriched-stops journeys, you will receive two webhooks with nearly identical
event_datevalues:- First webhook:
state: "finished"(without stop information,stop_id: null) - Second webhook:
state: "pick up"(with stop information for the last stop)
The
pick upevent'sevent_dateis set ~100 milliseconds earlier than thefinishedevent to allow proper chronological ordering. If either webhook is delayed or arrives out of sequence, useevent_dateto order them correctly. Both events refer to the same journey end and should be processed independently using yourjourney_id+statededuplication key. - First webhook:
-
is_driver_on_another_journeyanddriver_stop_before_pickup: Only present inhot-hiredstate events. When a driver is assigned but is currently finishing another trip, these fields indicate the hot-hire scenario. You may receive multiplehot-hiredevents as the data is enriched (see Hot-Hired Event below).
Root Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Always "journey-state" for state updates |
data | object | Yes | Journey event data (see table above) |
errors | array | Yes | Error details (empty for successful events) |
Important Notes
- Event Ordering: Always use
event_dateto determine the most recent event of each type. - Driver Assignment:
driver_idis populated once a driver is assigned to the journey, and stays populated for the events that follow while that driver is on it. It is also populated in thehireevent we send when an assigned driver is unassigned, where it identifies the driver we removed (see Hire Event). - State can go backwards: a journey that had a driver can return to
hireor reachnot foundif that driver is unassigned, and can then be assigned a different driver.hiredis not a terminal step, so a state machine that only moves forward will get stuck. This can happen several times in one journey. - Rider ID:
rider_idis always present in the payload but its value can benullwhen the rider is external to Cabify (not a registered Cabify user). Your integration should handlenullvalues for this field. - Null Values: Fields like
rider_id,finished_at,terminated_at, andfinish_reasonarenulluntil the journey reaches the corresponding state or when the information is not available. - Multiple Events: You may receive multiple events of the same type, always trust the latest based on
event_date. - Creating a new journey after cancellation: Creating a new journey after a previous one was cancelled or finished is allowed. Duplicate validation applies to webhook event delivery (e.g. same
journey_idandstate), not to the creation of new journeys.
Understanding state and finish_reason
state and finish_reason-
stateis the current step in the journey lifecycle. It answers "Where is the journey right now?" Possible values:"hire","hired","hot-hired","arrived","pick up","stop changed","finished","terminated","not found". Every webhook event includes astate. -
finish_reasonis only present when the journey has reached an end. It answers "Why did the journey end?" Possible values:"drop off","no show","rider cancel","driver cancel","not found". The value"driver cancel"means the system cancelled the journey (e.g. rider flagged for fraud, or other internal reasons); it is not triggered by the driver. It is typically set whenstateis"finished"(and is repeated in the later"terminated"event). For events that are not an end of the journey,finish_reasonisnull. -
How failures are communicated: it depends on the outcome:
- No driver found: we send two events. First a dedicated
state: "not found"event withfinish_reason: null, thenstate: "finished",finish_reason: "not found". Both refer to the same journey end; you can use either to detect the outcome. - First (or only) passenger no show, rider cancel, or driver cancel: we send a single terminal event,
state: "finished"withfinish_reasonset to the cause ("no show","rider cancel", or"driver cancel"). There is no separate event carrying that reason as astatevalue;"no show","rider cancel"and"driver cancel"never appear in thestatefield, only infinish_reason.
- No driver found: we send two events. First a dedicated
-
Single-stop vs multi-stop: For both,
stateis at journey level. For multi-stop journeys, per-stop detail (which stop, pickup vs dropoff vs no-show) comes fromstop_id,stop_contacts, andstop_action, not from differentstatevalues.
Examples
This section contains an example of each type of event associated with a change in the state of a journey.
Hire Event
In the hiring state you might receive multiple hire events. You should always trust the last event of that type received (checking event_date).
⚠️ Important: The hire state means the journey currently has no confirmed driver. It is sent when we are searching for one, when a driver is preassigned to a reservation, and also when a driver that had already been assigned is taken off the journey. For reservations where a driver has been preassigned, use preassigned_driver_id.
⚠️ Important: a hire event can carry a populated driver_id. That happens in exactly one case: the journey had a confirmed driver and we unassigned them, and driver_id is the driver we removed. Treat state: "hire" with a non-null driver_id as "the driver you were told about is no longer on this journey", and expect the search to start again. In every other hire event driver_id is null.
You will receive this event when:
- The journey is created.
- We are searching for a driver to assign to the journey.
- In case the journey is a reservation and a driver is preassigned to the journey. In this case the
preassigned_driver_idwill be populated. - In case the journey is a reservation and the preassigned driver is removed. In this case the
preassigned_driver_idwill benull. - A driver that was already assigned (you had received a
hiredevent) is unassigned from the journey. In this casedriver_idis the unassigned driver andpreassigned_driver_idisnull. Anot foundevent may follow if we cannot find a replacement.
Note: Once a driver is assigned and confirmed, you will receive a
hiredevent with thedriver_idpopulated. Thehiredstate always has adriver_idvalue.
Here is an example of a hire event payload for an ASAP journey (no preassigned driver):
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "hire",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:26:46.736462Z",
"driver_id": null,
"preassigned_driver_id": null,
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": null
},
"type": "journey-state",
"errors": []
}'Here is an example of a hire event payload for a reservation with a preassigned driver:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "hire",
"start_type": "reserved",
"start_at": "2020-04-28T18:00:00Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:26:46.736462Z",
"driver_id": null,
"preassigned_driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": null
},
"type": "journey-state",
"errors": []
}'Not found Event
The not found event occurs when we are unable to find a driver to assign to the journey. This primarily happens for ASAP journeys, but can also occur for reservations (though it is unusual, as we have automatic and manual internal systems to prevent reservations from being without a driver).
When you receive a not found event, the endpoint keep_searching can be called to keep searching for a driver. You can only call it before the journey has ended: once you receive a finished event (with finish_reason: "not found") or terminated, the journey is closed and keep_searching is no longer available for that journey.
For reservations: Drivers are typically preassigned when the journey is created. If 15 minutes before the start_at time the assigned driver cannot reach the rider, the system automatically searches for alternative drivers. You will receive hired events when drivers are assigned or reassigned. In rare cases where no driver can be found, you may receive a not found event.
Here is an example of a not found event payload:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "not found",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:28:22.736462Z",
"driver_id": null,
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": null
},
"type": "journey-state",
"errors": []
}'Hired Event
You will receive this event once a driver is assigned to the journey. The driver_id field will always be populated in this event.
Here is an example of a hired event payload:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "hired",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:26:46.736462Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": [40.4138, -3.7061]
},
"type": "journey-state",
"errors": []
}'Hot-Hired Event
You will receive this event when the assigned driver is currently completing another trip before heading to the pickup (hot-hire scenario). Unlike other states, multiple hot-hired events for the same journey are expected as information is enriched.
The sequence is:
- Hot-hire detected —
is_driver_on_another_journey: true,driver_stop_before_pickup: null - Hot-hire enriched —
is_driver_on_another_journey: true,driver_stop_before_pickup: {"loc": [lat, lng]} - Hot-hire ended —
is_driver_on_another_journey: false,driver_stop_before_pickup: null
Not all steps are guaranteed — you may skip directly from step 1 to step 2, or never receive hot-hire events at all. Always use event_date to determine the latest event.
Integrators that do not consume hot-hire data can safely ignore hot-hired events.
Here is an example of a hot-hired event payload:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "hot-hired",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:26:50.123456Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"is_driver_on_another_journey": true,
"driver_stop_before_pickup": {"loc": [40.4138, -3.7061]},
"loc": [40.4200, -3.7080]
},
"type": "journey-state",
"errors": []
}'Arrived Event
Once the driver reaches the first pick up location, or any intermediate stop, an arrived event is emitted.
Here is an example of an arrived event payload:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "arrived",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:28:07.831121Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"stop_id": "d3b05d77-646a-46e5-bfd0-cf69a370afb8",
"stop_contacts": [{
"name": "Carlos Jesús",
"mobile_cc": "34",
"mobile_num": "677777777"
}],
"loc": [40.4169, -3.7061]
},
"type": "journey-state",
"errors": []
}'Pick Up Event
While the journey is active, you might receive multiple pick up events. You should always trust the last event of that type received (checking event_date).
Important for multi-stop journeys: The pick up state is used for both passenger pickups and dropoffs in multi-stop journeys. When a passenger is dropped off at an intermediate stop, you will receive a pick up event with stop_contacts containing the passenger information and stop_action set to "dropoff". To distinguish between pickups and dropoffs, always check the stop_action field in stop_contacts. You will also receive a pick up event when the driver resumes the journey after pausing at a stop.
Here is an example of a pick up event payload:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "pick up",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:28:07.831121Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"stop_id": "d3b05d77-646a-46e5-bfd0-cf69a370afb8",
"stop_contacts": [{
"name": "Carlos Jesús",
"mobile_cc": "34",
"mobile_num": "677777777",
"stop_action": "pickup"
}],
"loc": [40.4169, -3.7061]
},
"type": "journey-state",
"errors": []
}'Finish Event
Once the passenger has reached their destination without an issue you will receive the finished event with value "drop off" in the finish_reason field.
In addition, you will receive this event as well when:
| Problem | finish_reason |
|---|---|
| The rider didn't show up at the pick up point | "no show" |
| The rider cancelled the journey | "rider cancel" |
| The system cancelled the journey | "driver cancel" |
Here is an example of a finish event payload when the journey has finished without any issues:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "finished",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": "2020-04-28T15:32:01Z",
"finish_reason": "drop off",
"event_date": "2020-04-28T15:32:04.025384Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": [40.4200, -3.7080]
},
"type": "journey-state",
"errors": []
}'Terminated Event
Finally, automatically after a certain amount of time, the journey is moved to Terminated state and its lifecycle ends.
Here is an example of a terminated event payload when the journey has been terminated without any issues:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": "2020-04-28T16:00:00Z",
"state": "terminated",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": "2020-04-28T15:32:01Z",
"finish_reason": "drop off",
"event_date": "2020-04-28T15:32:04.025384Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": null
},
"type": "journey-state",
"errors": []
}'Stop Changed Event
While the journey is active, you might receive multiple stop changed events.
This occurs when stops are changed.
Here is an example of a stop changed event payload:
curl -X POST
YOUR_URL
-H 'Content-Type: application/json'
-d '{
"data":{
"user_id": "591bbf5d7db7d84023ca3760ba1f1000",
"terminated_at": null,
"state": "stop changed",
"start_type": "asap",
"start_at": "2020-04-28T15:26:36Z",
"rider_id": "591bbf5d7db7d84023ca3760ba1f1000",
"journey_id": "3fad6e43719a48f3904b5f2d54865b1a",
"finished_at": null,
"finish_reason": null,
"event_date": "2020-04-28T15:32:04.025384Z",
"driver_id": "1d254ba6aff9ddd82ff5543ae85a88eb",
"created_at": "2020-04-28T15:26:36Z",
"client_id": "512cc654f0e342efcdd3fdfde9fd8000",
"loc": null
},
"type": "journey-state",
"errors": []
}'Single-Stop Journey Use Cases
The following use cases illustrate the event sequence for single-stop (one pickup, one dropoff) journeys. Each diagram shows the events we send with state and finish_reason for every step.
Use Case: Happy Path (Single Stop)
Scenario: Journey is created, a driver is found, passenger is picked up and dropped off successfully.
Event Sequence:
hire— searching for driver (state: "hire",finish_reason: null)hired— driver assigned (state: "hired",finish_reason: null)arrived— driver at pickup (state: "arrived",finish_reason: null)pick up— passenger picked up (state: "pick up",finish_reason: null)finished— passenger dropped off (state: "finished",finish_reason: "drop off")terminated— journey lifecycle ended (state: "terminated",finish_reason: "drop off")
sequenceDiagram
participant API as Cabify Webhook
participant Client
API->>Client: {"state": "hire", "finish_reason": null}
API->>Client: {"state": "hired", "finish_reason": null}
API->>Client: {"state": "arrived", "finish_reason": null}
API->>Client: {"state": "pick up", "finish_reason": null}
API->>Client: {"state": "finished", "finish_reason": "drop off"}
API->>Client: {"state": "terminated", "finish_reason": "drop off"}
Use Case: Hot-Hire (Driver Completing Another Trip)
Scenario: A driver is assigned but is currently finishing another trip. The system sends multiple hot-hired events as more information becomes available.
Event Sequence:
hire— searching for driverhired— driver assignedhot-hired— hot-hire detected (is_driver_on_another_journey: true,driver_stop_before_pickup: null)hot-hired— hot-hire enriched (is_driver_on_another_journey: true,driver_stop_before_pickup: {"loc": [lat, lng]})hot-hired— hot-hire ended (is_driver_on_another_journey: false)arrived→pick up→finished→terminated(normal flow continues)
sequenceDiagram
participant API as Cabify Webhook
participant Client
API->>Client: {"state": "hire", "finish_reason": null}
API->>Client: {"state": "hired", "finish_reason": null}
Note over API,Client: Hot-hire detected
API->>Client: {"state": "hot-hired", "is_driver_on_another_journey": true, "driver_stop_before_pickup": null}
API->>Client: {"state": "hot-hired", "is_driver_on_another_journey": true, "driver_stop_before_pickup": {"loc": [lat, lng]}}
Note over API,Client: Driver finishes other trip
API->>Client: {"state": "hot-hired", "is_driver_on_another_journey": false}
API->>Client: {"state": "arrived", "finish_reason": null}
API->>Client: {"state": "pick up", "finish_reason": null}
API->>Client: {"state": "finished", "finish_reason": "drop off"}
API->>Client: {"state": "terminated", "finish_reason": "drop off"}
Use Case: No Driver Found
Scenario: No driver is found before the search time limit. We send two events for this outcome. You can call keep_searching to retry only before you receive the finished event; after that, the journey is closed.
Event Sequence:
hire— searching for driver (state: "hire",finish_reason: null)- First event — no driver found (
state: "not found",finish_reason: null) - Second event — journey ended (
state: "finished",finish_reason: "not found")
Both events refer to the same journey end; you can use either to detect that no driver was found.
sequenceDiagram
participant API as Cabify Webhook
participant Client
API->>Client: {"state": "hire", "finish_reason": null}
API->>Client: {"state": "not found", "finish_reason": null}
API->>Client: {"state": "finished", "finish_reason": "not found"}
Use Case: Rider Cancel
Scenario: The rider cancels the journey (e.g. via app or via the cancel journey API). Can occur from hire, hired, or arrived — but only before the first pick up. In multi-stop journeys, arrived events are also sent at intermediate stops; however, cancellation is not allowed at that point because a pick up has already occurred. A cancel attempt after the first pick up will return a 409 Conflict with {"message": "can't cancel journey at current state"}.
Event Sequence:
hireand/orhired(and optionallyarrived)finished— journey ended by rider (state: "finished",finish_reason: "rider cancel")- Later:
terminated(state: "terminated",finish_reason: "rider cancel")
sequenceDiagram
participant API as Cabify Webhook
participant Client
API->>Client: {"state": "hire", "finish_reason": null}
API->>Client: {"state": "hired", "finish_reason": null}
API->>Client: {"state": "finished", "finish_reason": "rider cancel"}
API->>Client: {"state": "terminated", "finish_reason": "rider cancel"}
Use Case: System Cancel ("driver cancel")
Scenario: The system cancels the journey (e.g. rider flagged for fraud, low score, or other internal reasons). The finish_reason is sent as "driver cancel"; this is not triggered by the driver.
Event Sequence:
hire→hired(and optionallyarrived)finished— journey ended by system (state: "finished",finish_reason: "driver cancel")- Later:
terminated(state: "terminated",finish_reason: "driver cancel")
sequenceDiagram
participant API as Cabify Webhook
participant Client
API->>Client: {"state": "hire", "finish_reason": null}
API->>Client: {"state": "hired", "finish_reason": null}
API->>Client: {"state": "finished", "finish_reason": "driver cancel"}
API->>Client: {"state": "terminated", "finish_reason": "driver cancel"}
Use Case: First (or Only) Passenger No Show
Scenario: Driver arrives at the first pickup; the passenger does not show up within the courtesy time.
Event Sequence:
hire→hired→arrivedfinished— journey ended by no-show (state: "finished",finish_reason: "no show")- Later:
terminated(state: "terminated",finish_reason: "no show")
sequenceDiagram
participant API as Cabify Webhook
participant Client
API->>Client: {"state": "hire", "finish_reason": null}
API->>Client: {"state": "hired", "finish_reason": null}
API->>Client: {"state": "arrived", "finish_reason": null}
API->>Client: {"state": "finished", "finish_reason": "no show"}
API->>Client: {"state": "terminated", "finish_reason": "no show"}
Multi-Stop Journey Events
For multi-stop and multi-passenger journeys, the standard arrived and pick up events are used for all stops. The key difference is the stop_action field in stop_contacts that indicates whether the action at each stop is a "pickup" or "dropoff".
Note: In multi-stop journeys, you will receive arrived events when the driver reaches each stop (first pickup and all intermediate stops), and pick up events when passengers are picked up or dropped off at any stop. Always check the stop_action field to determine the action type.
No Show at Intermediate Stops
If a passenger doesn't show up at an intermediate stop, the driver can mark them as "no show" using the Proof of Pickup feature. The vehicle then resumes towards the next stop, and you receive a pick up event with:
state:"pick up"finish_reason:null- That passenger's entry in
stop_contactshasstop_action:null(indicates they were not picked up) - The system will automatically remove the no-show passenger's remaining stops from the journey
Note:
"no show"is never astatevalue; it only ever appears as afinish_reason, and only when the journey finishes with a no-show at the first pickup location. For intermediate stops, usestop_action: nullin apick upevent to detect no-shows.
Multi-Stop Journey Use Cases
The following use cases illustrate how events work in multi-stop, multi-passenger journeys:
Use Case 1: Happy Path - Two Passengers, Two Dropoffs
Scenario: Driver picks up two passengers and drops off both successfully.
Event Sequence:
- Stop 1 - Driver arrives:
arrivedevent withstop_idandstop_contacts: null - Stop 1 - Passenger 1 picked up:
pick upevent withstop_contactscontaining passenger 1 info andstop_action: "pickup" - Stop 2 - Driver arrives:
arrivedevent withstop_idandstop_contacts: null - Stop 2 - Passenger 2 picked up:
pick upevent withstop_contactscontaining passenger 2 info andstop_action: "pickup" - Stop 3 - Driver arrives:
arrivedevent for passenger 1 dropoff - Stop 3 - Passenger 1 dropped off:
pick upevent withstop_contactscontaining passenger 1 info andstop_action: "dropoff" - Stop 4 - Passenger 2 dropped off:
finishedevent withfinish_reason: "drop off"andstop_contactscontaining passenger 2 info withstop_action: "dropoff"
sequenceDiagram
participant API as Cabify Webhook
participant Client
Note over Client,API: Stop 1: arrived and passenger 1 pick up
API->>Client: {"state": "arrived", "finish_reason": null, "stop_id": "...", "stop_contacts": null}
API->>Client: {"state": "pick up", "finish_reason": null, "stop_id": "...", "stop_contacts": [{"stop_action": "pickup", "name": "John Doe", "mobile_cc": "34", "mobile_num": "677777777"}]}
Note over Client,API: Stop 2: arrived and passenger 2 pick up
API->>Client: {"state": "arrived", "finish_reason": null, "stop_id": "...", "stop_contacts": null}
API->>Client: {"state": "pick up", "finish_reason": null, "stop_id": "...", "stop_contacts": [{"stop_action": "pickup", "name": "Jane Doe", "mobile_cc": "33", "mobile_num": "12"}]}
Note over Client,API: Stop 3: passenger 1 drop off
API->>Client: {"state": "arrived", "finish_reason": null, "stop_id": "...", "stop_contacts": null}
API->>Client: {"state": "pick up", "finish_reason": null, "stop_id": "...", "stop_contacts": [{"stop_action": "dropoff", "name": "John Doe", "mobile_cc": "34", "mobile_num": "677777777"}]}
Note over Client,API: Stop 4: passenger 2 drop off
API->>Client: {"state": "finished", "finish_reason": "drop off", "stop_id": "...", "stop_contacts": [{"stop_action": "dropoff", "name": "Jane Doe", "mobile_cc": "33", "mobile_num": "12"}]}
Use Case 2: First Passenger No Show
Scenario: First passenger doesn't show up at the first pickup.
Event Sequence:
- Stop 1 - Driver arrives:
arrivedevent (state: "arrived",finish_reason: null) - Journey ended:
finishedevent (state: "finished",finish_reason: "no show")
sequenceDiagram
participant API as Cabify Webhook
participant Client
Note over Client,API: Stop 1: arrived and passenger 1 no show
API->>Client: {"state": "arrived", "finish_reason": null, "stop_id": "...", "stop_contacts": []}
API->>Client: {"state": "finished", "finish_reason": "no show"}
Use Case 3: Second Passenger No Show
Scenario: First passenger is picked up successfully, but second passenger doesn't show up.
Event Sequence:
- Stop 1 - Driver arrives:
arrivedevent - Stop 1 - Passenger 1 picked up:
pick upevent withstop_action: "pickup" - Stop 2 - Driver arrives:
arrivedevent - Stop 2 - Passenger 2 no show:
pick upevent withstop_action: null(indicates no-show)- The system automatically removes passenger 2's remaining stops from the journey
- Stop 3 - Passenger 1 dropped off:
finishedevent withfinish_reason: "drop off"andstop_action: "dropoff"
sequenceDiagram
participant API as Cabify Webhook
participant Client
Note over Client,API: Stop 1: arrived and passenger 1 pick up
API->>Client: {"state": "arrived", "finish_reason": null, "stop_id": "...", "stop_contacts": null}
API->>Client: {"state": "pick up", "finish_reason": null, "stop_id": "...", "stop_contacts": [{"stop_action": "pickup", "name": "John Doe", "mobile_cc": "34", "mobile_num": "677777777"}]}
Note over Client,API: Stop 2: arrived and no show
API->>Client: {"state": "arrived", "finish_reason": null, "stop_id": "...", "stop_contacts": null}
API->>Client: {"state": "pick up", "finish_reason": null, "stop_id": "...", "stop_contacts": [{"stop_action": null, "name": "Jane Doe", "mobile_cc": "33", "mobile_num": "12"}]}
Note over API: Passenger 2's remaining stops<br/>automatically removed
Note over Client,API: Stop 3: passenger 1 drop off
API->>Client: {"state": "finished", "finish_reason": "drop off", "stop_id": "...", "stop_contacts": [{"stop_action": "dropoff", "name": "John Doe", "mobile_cc": "34", "mobile_num": "677777777"}]}
Important Notes:
- No-show at intermediate stops: When a passenger doesn't show up at an intermediate stop, you receive a
pick upevent; that passenger's entry instop_contactshasstop_action: null. - When a passenger is marked as no-show at an intermediate stop, their remaining stops are automatically removed from the journey
- The
stop_actionfield instop_contactsis essential for distinguishing between pickups ("pickup"), dropoffs ("dropoff"), and no-shows (null) in multi-stop journeys
Updated 14 days ago
