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:

FieldTypeRequiredDescriptionExample Values
client_idstringYesYour Cabify client identifier"512cc654f0e342efcdd3fdfde9fd8000"
journey_idstringYesUnique journey identifier"3fad6e43719a48f3904b5f2d54865b1a"
user_idstringYesUser who created the journey"591bbf5d7db7d84023ca3760ba1f1000"
rider_idstring or nullYesPassenger identifier. Will be null if the rider is external to Cabify (not a registered Cabify user)"591bbf5d7db7d84023ca3760ba1f1000" or null
statestringYesCurrent journey state"hire", "hired", "hot-hired", "not found", "arrived", "pick up", "stop changed", "finished", "terminated"
event_datestring (ISO 8601)YesWhen the event occurred"2020-04-28T15:26:46.736462Z"
created_atstring (ISO 8601)YesWhen the journey was created"2020-04-28T15:26:36Z"
start_typestringYesJourney start type"asap", "reserved"
start_atstring (ISO 8601)YesScheduled start time"2020-04-28T15:26:36Z"
driver_idstringNoAssigned 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_idstringNoPreassigned 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_atstring (ISO 8601)NoWhen journey finished"2020-04-28T15:32:01Z" or null
terminated_atstring (ISO 8601)NoWhen journey was terminated"2020-04-28T16:00:00Z" or null
finish_reasonstringNoReason for journey completion"drop off", "no show", "rider cancel", "driver cancel", "not found"
locarrayNoDriver's location when the state changed, as [lat, lng][40.4169, -3.7061] or null
stop_idstringNoStop id that triggered the state change"d3b05d77-646a-46e5-bfd0-cf69a370afb8" or null
stop_contactsarrayNoAn 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_journeybooleanNotrue when the assigned driver is completing another trip before heading to pickup. Only present in hot-hired events.true, false
driver_stop_before_pickupobject or nullNoThe 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 is null when:

    • No driver has been assigned yet (hire, not found)
    • For finished events, location is only populated for finish_reason: "drop off" and finish_reason: "no show"; for other finish reasons, it is null
  • stop_id and stop_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 be null as they are not associated with a specific stop. We send notifications for every arrived and pick up event (including every stop in multi-stop journeys); you can rely on receiving them for validation. Even for arrived and pick up events, stop_id may be null if:

    • 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_id in webhook events is resolved by matching the driver's GPS coordinates to the nearest journey stop within a 150-meter radius. This means the stop_id returned 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 the stop_contacts information (passenger name, phone number) or the event sequence order rather than relying solely on stop_id equality.

  • stop_action: For multi-stop journeys, the stop_contacts array includes a stop_action field 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 in pick up events. For arrived events, stop_action is not included in stop_contacts as 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 up event carrying that stop's stop_id / stop_contacts / stop_action — the same way intermediate stops are reported. It is not folded into the finished event. The finished and terminated events remain without stop information (stop_id and stop_contacts are null). This applies to journeys that use per-stop contacts (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_date values:

    • First webhook: state: "finished" (without stop information, stop_id: null)
    • Second webhook: state: "pick up" (with stop information for the last stop)

    The pick up event's event_date is set ~100 milliseconds earlier than the finished event to allow proper chronological ordering. If either webhook is delayed or arrives out of sequence, use event_date to order them correctly. Both events refer to the same journey end and should be processed independently using your journey_id + state deduplication key.

  • is_driver_on_another_journey and driver_stop_before_pickup: Only present in hot-hired state events. When a driver is assigned but is currently finishing another trip, these fields indicate the hot-hire scenario. You may receive multiple hot-hired events as the data is enriched (see Hot-Hired Event below).

Root Level Fields

FieldTypeRequiredDescription
typestringYesAlways "journey-state" for state updates
dataobjectYesJourney event data (see table above)
errorsarrayYesError details (empty for successful events)

Important Notes

  • Event Ordering: Always use event_date to determine the most recent event of each type.
  • Driver Assignment: driver_id is 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 the hire event 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 hire or reach not found if that driver is unassigned, and can then be assigned a different driver. hired is 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_id is always present in the payload but its value can be null when the rider is external to Cabify (not a registered Cabify user). Your integration should handle null values for this field.
  • Null Values: Fields like rider_id, finished_at, terminated_at, and finish_reason are null until 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_id and state), not to the creation of new journeys.

Understanding state and finish_reason

  • state is 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 a state.

  • finish_reason is 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 when state is "finished" (and is repeated in the later "terminated" event). For events that are not an end of the journey, finish_reason is null.

  • How failures are communicated: it depends on the outcome:

    • No driver found: we send two events. First a dedicated state: "not found" event with finish_reason: null, then state: "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" with finish_reason set to the cause ("no show", "rider cancel", or "driver cancel"). There is no separate event carrying that reason as a state value; "no show", "rider cancel" and "driver cancel" never appear in the state field, only in finish_reason.
  • Single-stop vs multi-stop: For both, state is at journey level. For multi-stop journeys, per-stop detail (which stop, pickup vs dropoff vs no-show) comes from stop_id, stop_contacts, and stop_action, not from different state values.

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:

  1. The journey is created.
  2. We are searching for a driver to assign to the journey.
  3. In case the journey is a reservation and a driver is preassigned to the journey. In this case the preassigned_driver_id will be populated.
  4. In case the journey is a reservation and the preassigned driver is removed. In this case the preassigned_driver_id will be null.
  5. A driver that was already assigned (you had received a hired event) is unassigned from the journey. In this case driver_id is the unassigned driver and preassigned_driver_id is null. A not found event may follow if we cannot find a replacement.

Note: Once a driver is assigned and confirmed, you will receive a hired event with the driver_id populated. The hired state always has a driver_id value.

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:

  1. Hot-hire detected — is_driver_on_another_journey: true, driver_stop_before_pickup: null
  2. Hot-hire enriched — is_driver_on_another_journey: true, driver_stop_before_pickup: {"loc": [lat, lng]}
  3. 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:

Problemfinish_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:

  1. hire — searching for driver (state: "hire", finish_reason: null)
  2. hired — driver assigned (state: "hired", finish_reason: null)
  3. arrived — driver at pickup (state: "arrived", finish_reason: null)
  4. pick up — passenger picked up (state: "pick up", finish_reason: null)
  5. finished — passenger dropped off (state: "finished", finish_reason: "drop off")
  6. 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:

  1. hire — searching for driver
  2. hired — driver assigned
  3. hot-hired — hot-hire detected (is_driver_on_another_journey: true, driver_stop_before_pickup: null)
  4. hot-hired — hot-hire enriched (is_driver_on_another_journey: true, driver_stop_before_pickup: {"loc": [lat, lng]})
  5. hot-hired — hot-hire ended (is_driver_on_another_journey: false)
  6. 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:

  1. hire — searching for driver (state: "hire", finish_reason: null)
  2. First event — no driver found (state: "not found", finish_reason: null)
  3. 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:

  1. hire and/or hired (and optionally arrived)
  2. finished — journey ended by rider (state: "finished", finish_reason: "rider cancel")
  3. 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:

  1. hire → hired (and optionally arrived)
  2. finished — journey ended by system (state: "finished", finish_reason: "driver cancel")
  3. 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:

  1. hire → hired → arrived
  2. finished — journey ended by no-show (state: "finished", finish_reason: "no show")
  3. 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_contacts has stop_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 a state value; it only ever appears as a finish_reason, and only when the journey finishes with a no-show at the first pickup location. For intermediate stops, use stop_action: null in a pick up event 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:

  1. Stop 1 - Driver arrives: arrived event with stop_id and stop_contacts: null
  2. Stop 1 - Passenger 1 picked up: pick up event with stop_contacts containing passenger 1 info and stop_action: "pickup"
  3. Stop 2 - Driver arrives: arrived event with stop_id and stop_contacts: null
  4. Stop 2 - Passenger 2 picked up: pick up event with stop_contacts containing passenger 2 info and stop_action: "pickup"
  5. Stop 3 - Driver arrives: arrived event for passenger 1 dropoff
  6. Stop 3 - Passenger 1 dropped off: pick up event with stop_contacts containing passenger 1 info and stop_action: "dropoff"
  7. Stop 4 - Passenger 2 dropped off: finished event with finish_reason: "drop off" and stop_contacts containing passenger 2 info with stop_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:

  1. Stop 1 - Driver arrives: arrived event (state: "arrived", finish_reason: null)
  2. Journey ended: finished event (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:

  1. Stop 1 - Driver arrives: arrived event
  2. Stop 1 - Passenger 1 picked up: pick up event with stop_action: "pickup"
  3. Stop 2 - Driver arrives: arrived event
  4. Stop 2 - Passenger 2 no show: pick up event with stop_action: null (indicates no-show)
    • The system automatically removes passenger 2's remaining stops from the journey
  5. Stop 3 - Passenger 1 dropped off: finished event with finish_reason: "drop off" and stop_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 up event; that passenger's entry in stop_contacts has stop_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_action field in stop_contacts is essential for distinguishing between pickups ("pickup"), dropoffs ("dropoff"), and no-shows (null) in multi-stop journeys

Did this page help you?