taxiID Integration API
This is the reference for developers building an integration into a company's taxiID account —
quoting, creating and tracking rides, and receiving webhook events. All requests are made to
integrations.taxiid.nl; you never call api.taxiid.nl directly. All money is integer cents, all
timestamps are ISO-8601 UTC.
Pick the tab that matches what you're building — each only shows the calls you actually need. The Objects section below is shared reference material and always stays visible, whichever tab you pick — it documents the full shape of the data the calls further down (in Administration, Dispatch and Ride creation) send and receive, so it isn't repeated endpoint by endpoint.
Objects
The JSON shown next to each call below is trimmed to what that call is about — this section is the full shape of every object type, so you know what else you can read (and, where noted, write) that isn't shown inline everywhere.
Field names, types and enums here mirror the taxiID core API's models exactly (the same names you
see in the api.taxiid.nl explorer), so a ride you read through the Integration API has the same
fields as the ride in the portal. Nested objects marked freeform are stored as-is and not
schema-validated.
Ride
A ride is the central object; every call above either creates one, reads one, or reports a change
to one. Most fields are set by taxiID as the ride progresses (arrival ETA, assigned driver,
computed price) — you only send the fields marked input when creating one via POST /v1/rides.
Every ride also carries createdAt and modifiedAt (ISO-8601, set by taxiID).
| Property | Type | Input? | Description |
|---|---|---|---|
id |
string | no | taxiID's ride identifier. Use this in every later call about the ride. |
intId |
integer | no | Human-friendly sequential ride number, shown in the portal and to drivers. |
reference |
string | optional | Free-text booking reference shown in the portal and on invoices. |
isTestBooking |
boolean | optional | Marks the ride as a test; defaults to false. |
type |
string | no | Kind of job; defaults to ride. |
status |
string enum | no | quoteRequested/quoted/requested (pre-process, not yet live/dispatchable; quote_expired is where an unaccepted quote ends) → unassigned/accepted → assigned → active/driving_to_pickup → arrived_at_pickup → started → completed; or canceled/canceled_driver/canceled_passenger from most non-final states. Full enum: quoteRequested, quoted, requested, accepted, unassigned, assigned, active, driving_to_pickup, arrived_at_pickup, started, completed, canceled, canceled_driver, canceled_passenger, quote_expired. A ride created without a status starts as unassigned. completed, the three canceled* values and quote_expired are terminal — no further transitions. A dispatch offer (waiting on a specific driver to accept) is a separate, sub-status concept that does not appear in this field — see Webhook: ride.status_changed and POST /v1/rides/{id}/offer. |
cancellationReason |
string | optional | Why the ride was cancelled; also drives whether a cancellation charge applies. |
requestedDate |
ISO-8601 datetime | yes | When the ride should start. Omit for an immediate/ASAP ride. |
pickupDate |
ISO-8601 datetime | no | Actual/scheduled pickup moment as dispatch has it. |
requestedDestinationDate |
ISO-8601 datetime | optional | When the passenger needs to be at the destination (arrive-by bookings). |
requestedScheduleDate |
ISO-8601 datetime | no | Moment the ride was put on the dispatch schedule. |
dispatchTimeToPickup |
ISO-8601 datetime | no | When dispatch is due to start driving to the pickup. |
timezone |
number | optional | UTC offset of the pickup location, in hours. |
departure |
Address | yes | The pickup location. (origin on a ride is not the pickup — see below.) |
destination |
Address | yes | The drop-off. Optional for some vehicle types (e.g. a driver-at-disposal booking). |
stopOvers |
array of Address | optional | Intermediate stops between departure and destination, in order. Defaults to []. |
origin |
Origin | no | The booking channel the ride came in through (e.g. the portal, an integration) — not a location. |
passengerId |
string | no | The Passenger riding. Created/linked by taxiID from the passenger details you send. |
passengerCount |
integer | optional | Number of people travelling. Affects vehicle-type eligibility. Defaults to 4. |
luggageCount |
integer | optional | Number of luggage items. |
businessRide |
boolean | optional | Tags the ride as a one-off "business ride". |
businessInfo |
{companyName, address} |
optional | Company name and postal address shown alongside a business ride; address is {streetName, houseNumber, postalCode, city, countryCode}. |
flightNumber |
string | optional | For airport pickups — enables flight-tracked pickup timing. |
note |
string | optional | Free-text remark on the booking (e.g. "call on arrival"). |
driverNote |
string | optional | Instruction shown to the driver. |
operatorNote |
string | optional | Internal note for the dispatcher/operator; never shown to the passenger or driver. |
language |
string | optional | Passenger's language for SMS/email/driver-app messaging. Defaults to EN. |
vehicleType |
string | optional | No fixed platform-wide enum — a plain string matched against the vehicle types/products the company has configured in its own pricing catalog. There is currently no endpoint to discover a given company's allowed values. Open question for the API team: should a GET /v1/vehicle-types discovery endpoint be added? |
products |
array, freeform | optional | The priced product lines on the ride. Defaults to []. |
debtorCode |
string | optional | Overrides which of the company's debtors is billed, if the company bills multiple debtors. |
debtorId / couponId / driverTariffId |
string | no | Resolved debtor, applied coupon and driver tariff. |
foreignBookingId |
string | optional, echoed back | Your own booking reference. Not interpreted by taxiID — round-tripped as-is onto every webhook so you can match events back to your system. |
foreignDispatchId |
string | optional | The ride's id in the other party's dispatch system, when it was handed to or from a partner. |
quoteId |
string | optional (POST /v1/rides only) |
The id from a prior POST /v1/quotes response, so the ride is created at that locked-in price. Open question for the API team: what happens when it's omitted, and whether a stored quote id exists at all — the core pricing endpoint is stateless today, so this would need to be introduced by integrations.taxiid.nl's still-unbuilt /v1/quotes. It is not a stored Ride field. |
paymentMeta |
PaymentMeta | optional | How the ride is paid for. Defaults to invoicing the company's account if omitted. |
price |
Price | no | Computed by taxiID, normally from a prior POST /v1/quotes. Never send your own price on create. |
priceMethod |
string enum | no | How the price is determined: meter (default; legacy alias onMeter), fixed, hourlyMeter, hourlyFixed. |
calculationMethod |
string enum | no | manual (default) or group_pricing_rules. |
priceChangedReason |
string enum | no | "" (default), invalid_mileage or invalid_duration — why the price was adjusted after the ride. |
priceMeta |
object, freeform | no | Pricing audit data (which rule/product produced the price). |
driverPrice / driverSettlement / commission |
{total, currency, …} |
no | What the driver is paid, what is settled to them, and the platform/fleet commission. |
commissionRules |
array, freeform | no | The commission rules applied to the ride. Defaults to []. |
taxiMeter |
string enum | optional | Where the fare meter runs: app, car or none. |
metricsMethod |
string enum | optional | How distance/duration are measured: onMeter, fixed, odoMeter, googleRoute (default), driverAppTracking, external. |
metrics / actualMetrics |
Metrics | no | Expected route metrics, and the metrics actually driven. |
eta / etp |
object, freeform | no | Live estimate of arrival at pickup (eta) and estimated time of pickup (etp); only present once a driver is en route. |
estimations |
object, freeform | no | Route/time estimates computed at booking. |
events |
JobEvents | no | Timestamped status history — the audit trail behind the current status. |
vehicleStatus |
object, freeform | no | Live status of the assigned vehicle. |
holdWindow |
{productId, start, end, calculationType} |
no | The product-capacity window the ride currently blocks; set asynchronously after create/update, so it can be briefly absent or stale. |
pendingChange |
object, nullable | no | A passenger edit awaiting payment of its price increase. At most one at a time. |
meta |
object, freeform | optional | Your own key/value data, stored and returned untouched. Not shown to the driver or passenger — use note/driverNote for that. |
foreignMeta |
object, freeform | no | The equivalent bag the other side (driver app / dispatch) attaches; read-only to you. |
resources |
object, freeform | no | Additional resources attached to the ride. |
companyId |
string | no | The company account the ride belongs to — always your own. |
ownerId / ownerType |
string | no | Who owns the ride and its discriminator (Company or Driver). |
serviceProviderId / serviceProviderType |
string | no | Who performs the ride (Company or Driver). |
driverId / vehicleId |
string | no | Set once a driver is assigned (from status:'assigned' onward). Absent before that. |
operatorId / dispatcherId |
string | no | The operator and dispatcher handling the ride. |
creatorId / lastModifierId / lastModifierType |
string | no | Who created and who last modified the ride (and what kind of actor). |
pluginInstallId |
string | no | The integration/channel installation the ride arrived through. |
rideCombinationId / sequenceIndex |
string / integer | no | The combined route the ride belongs to and its position in it. |
recurringOrderId |
string | no | Set when the ride was generated from a recurring order. |
rideLogId / userId / shiftId |
string | no | Internal links to the ride log, the booking user and the driver shift. |
dispatchSettings / triggers / dispatcherAccessLevel |
object | no | Internal dispatch state. Documented for completeness; don't rely on them in an integration (dispatchSettings and triggers are hidden from API responses). |
Address
Used for a ride's departure, destination and every entry of stopOvers, and for the same
fields on POST /v1/quotes. gps is what taxiID routes and prices on; the street/city fields are
shown to the driver and passenger.
taxiID validates and enriches addresses on ride creation. Confirmed in api.taxiid.nl
(AddressValidationService): for Dutch addresses it checks the postcode and house number against
pro6pp, the BAG (Kadaster/PDOK) and Google reverse-geocoding, so a ride you create can come back
with a corrected postalCode, streetName or houseNumber, or with missing fields filled in from
a known location (a favourite address, or a pin within 50 m). Send as complete an address as you
have — gps plus street, house number, postal code, city and countryCode — and do not rely on the
returned address being byte-identical to what you sent. If an address stays unusable after that
(for example no gps and no street/city, only a description), the ride is refused. A geocoder
outage never blocks a ride. Open question for the API team: the HTTP status and error body of
that refusal, and whether POST /v1/quotes applies the same validation, are not confirmed (the
core model marks gps required, but the validator accepts an address without it when it has
enough identity to resolve).
| Property | Type | Input? | Description |
|---|---|---|---|
gps |
{lat, lng} |
yes | WGS-84 coordinates (GeoPoint). Used for routing and pricing. |
streetName / houseNumber |
string | optional | Street and house number. |
postalCode / city |
string | optional | Postal code and city. |
countryCode |
string | optional | Country of the address, ISO 3166-1 alpha-2 (e.g. NL). Used for local address-format rules and duty/VAT handling. The stored default is EN, which is not a valid ISO code — always send it explicitly. |
type |
string enum | optional | generic (default), airport, trainStation or poi. |
code |
string | optional | Code for an airport/station pickup (e.g. IATA AMS), alongside flightNumber on the ride. |
poiCode |
string | optional | Point-of-interest code. |
synonym |
string | optional | Familiar name of the place (e.g. "Hotel Okura"). |
internationalAlias |
string | optional | Name of the place in English/international form. |
description |
string | optional | Free-text description of the location. |
placesId |
string | optional | Google Places id, if the address was picked from Places. |
gpsSpeed / gpsBearing / gpsAccuracy |
number | no | Speed, bearing and accuracy of the GPS fix, when the address came from a device. |
gpsTime |
ISO-8601 datetime | no | Time of the GPS fix. |
gpsOrigin / gpsPriority |
string | no | Where the fix came from and its priority. |
Origin
The booking channel a ride came in through — what Ride.origin holds. Read-only to you.
| Property | Type | Description |
|---|---|---|
id |
string | Origin identifier. |
name |
string | Channel name (e.g. the portal, a booking widget, an integration). |
originId |
string | The id of the channel in the external system, if any. |
pluginInstallId |
string | The integration installation this channel belongs to. |
Passenger
A ride references its passenger through passengerId; when you create a ride you send the
passenger's details and taxiID finds or creates the matching record. Required fields are
fname, lname, email and phoneNumber.
| Property | Type | Input? | Description |
|---|---|---|---|
id |
string | no | Passenger identifier. |
fname / lname |
string | yes | First and last name. Shown to the driver. |
email |
string | yes | Used for ride confirmations/receipts. |
phoneNumber |
string (E.164) | yes | Used for driver-arrival SMS and driver contact. Always include the country code. |
language |
string | optional | Passenger's language; defaults to en. |
foreignId |
string | optional | The passenger's id in your system, kept for matching. |
companyId / realm |
string | no | The company the passenger belongs to, and the branding realm they signed up under. |
addressId |
string | no | The passenger's saved address. |
branding |
object, freeform | no | Branding applied to the passenger's emails/app. |
stripeCustomerId |
string | no | The passenger's Stripe customer, if they pay online. |
lastLogin / inviteSend |
ISO-8601 datetime | no | Last self-service login; when the passenger-app invitation was sent. |
isDeleted / deletedAt |
boolean / ISO-8601 datetime | no | Soft-delete marker. |
The password hash and reset token fields on the model are never returned by any call.
Price
Always computed by taxiID (from POST /v1/quotes or on ride completion) — never sent by you.
| Property | Type | Description |
|---|---|---|
total |
integer | Total price, in cents (matches the workspace-wide money convention). |
currency |
string (ISO 4217) | Almost always EUR. |
breakdown |
PriceBreakdown | Optional itemisation, see below — present on completed rides, may be absent on a fresh quote. |
PriceBreakdown has the keys route (always present), toll, parking, waiting, discount,
cleaning and additional. Each is {total, taxPercentage} — total in cents and
taxPercentage the VAT rate applied to that line.
PaymentMeta
| Property | Type | Input? | Description |
|---|---|---|---|
type |
string enum | optional | The platform-wide bucket: inTaxi, invoice (bill the company, default), or online. There is no separate cash/card value — inTaxi covers any in-vehicle payment and online covers a passenger-paid-online booking. Legacy callers may send the bucket as origin; taxiID maps it to type on create. Open question for the API team: does type:'online' trigger a live PSP charge synchronously at ride creation, or only label the ride for later settlement? Not confirmed — don't document a card value as available until it is. |
method |
string | optional | How it is paid within the bucket (e.g. onAccount for an invoice booking). |
moment |
string | optional | When it is paid (e.g. afterRide). |
description |
string | optional | Human-readable label for the payment choice. |
reference |
string | optional | Your own payment/reference note, echoed back untouched — same pattern as foreignBookingId. |
Metrics
Used for a ride's metrics (expected) and actualMetrics (driven).
| Property | Type | Description |
|---|---|---|
distance |
number | Distance, in the unit given by distanceFormat. |
duration |
number | Trip duration. |
waitingTime |
number | Time spent waiting. |
startMileage / endMileage |
number | Odometer readings, when metricsMethod is odoMeter. |
distanceFormat |
string | Unit of distance; defaults to km. |
JobEvents
A ride's events object — one entry per status the ride has reached, each a timestamped
location stamp: {city?, streetName?, postalCode?, houseNumber?, gps, date}. Keys present only once
reached: booked (set at creation), unassigned, assigned, driving_to_pickup,
arrived_at_pickup, started, arrived_at_destination, completed, canceled.
Webhook envelope
Every webhook delivery (see Dispatch → Verifying a webhook) shares this outer shape; data's
contents depend on event.
| Property | Type | Description |
|---|---|---|
event |
string enum | ride.created, ride.status_changed, or ride.updated (opt-in). |
data |
object | For ride.created/ride.updated, the Ride fields that changed plus id. For ride.status_changed, {id, status, previousStatus}. |
Administration
Setup and account calls. Every integration needs these first, regardless of what it does.
Authentication
Subject: send Authorization: Bearer <api-key> on every request. Get your key from the portal
under Booking channels → Developer API; it is shown once at creation/rotation time.
Enabling Developer API
Subject: the key/webhook are set up from the portal, not via this API — confirmed against
api.taxiid.nl's shipped Developer API channel (its own /developer-api routes, JWT-authenticated,
called by the portal UI on your behalf; never called directly by your integration).
- Attach the
developer_apiproduct to your contract (portal Offering → Contract) — this is the entitlement check every step below requires. - Enable the channel (portal Booking channels → Developer API, "Enable" button) — mints
your API key and shows it once. Enabling a second time while already enabled is rejected
(
409, see Errors below). - Rotate your key any time from the same page — the previous key stops working immediately, and the new one is shown once.
- Set your webhook URL and events — a webhook secret is minted the first time you set a URL and is never shown again automatically; use "Rotate webhook secret" to get a new one (also shown once).
- Disable removes the whole channel — your key and webhook config are both gone; re-enabling afterwards mints a fresh key.
GET /v1/me
Subject: your account's status — company name, entitlement, enabled webhook events, and your current rate limit.
Confirmed groundwork, endpoint itself still unbuilt: integrations.taxiid.nl doesn't expose
/v1/me yet, but the internal contract it would read from is live —
api.taxiid.nl's gateway-facing key-resolution call already returns
{pluginInstallId, companyId, entitled, rateLimitPerMinute, webhook:{url, events}} for a given
API key. companyName isn't part of that payload (it would need a separate company lookup in
integrations.taxiid.nl itself), so treat every field below as expected except companyName,
which needs to be added when this endpoint is actually built.
Rate limits
Subject: 120 requests/minute per API key by default — confirmed as api.taxiid.nl's
DEFAULT_RATE_LIMIT_PER_MINUTE, applied unless your account has a custom
rateLimitPerMinute configured. A 429 response carries Retry-After in seconds; back off and
retry.
Errors
Subject: every error is {"error":{"statusCode","code","message"}}. Confirmed platform-wide
codes: 401 unknown/rotated-out key, 402 not entitled (contract product not attached), 409
Developer API already enabled (only relevant to the portal's own enable call, see Enabling
Developer API above), 429 rate limited.
Open question for the API team: the booking-specific codes below (invalid/missing fields,
invalid coordinates, ride/quote not found, invalid state transitions) don't exist in any shipped
code yet — integrations.taxiid.nl's /v1/* surface is unbuilt — but they follow the same
{statusCode, code, message} envelope and the conventions api.taxiid.nl already uses elsewhere
in this codebase (a 404 when a referenced id isn't found or isn't yours, a 409-style rejection
of an invalid state transition). Treat these as the expected shape pending confirmation, not a
guarantee:
| Status | code |
When |
|---|---|---|
400 |
invalid_request |
A required field is missing, or malformed (e.g. gps.lat/gps.lng outside valid range). |
404 |
ride_not_found |
{id} doesn't exist, or exists but isn't scoped to your company. |
404 |
quote_not_found |
quoteId doesn't exist, or isn't yours. |
409 |
quote_expired |
quoteId is past its expiry (see POST /v1/quotes). |
409 |
quote_already_used |
quoteId was already consumed by an earlier POST /v1/rides. |
409 |
invalid_state_transition |
E.g. cancelling a ride that's already completed or otherwise terminal (see POST /v1/rides/{id}/cancel). |
Subject: 402 not_entitled means the company hasn't attached the required contract product —
confirmed directly in api.taxiid.nl's DeveloperApiService (DEVELOPER_API_SKU = 'developer_api'), matching the seed catalog's "Developer API-koppeling" product. A company
enables it from the portal under Booking channels → Developer API, which attaches that
product to the company's contract (the same accept-and-attach flow used for other paid
integrations like Taxsys) before the enable/API-key routes become usable — see Enabling
Developer API above for the exact step sequence.
Webhooks setup
Subject: webhook URL, secret, and subscribed events are configured in the portal (see Enabling
Developer API above), not via this API. The default subscribed events if you never change them
are ride.created and ride.status_changed — confirmed as api.taxiid.nl's
DEFAULT_WEBHOOK_EVENTS. Each delivery is signed — see Dispatch for verifying the signature.
Dispatch
Calls for integrations that monitor or hand off rides to drivers/fleets, rather than creating them.
GET /v1/rides
Subject: list your rides, filterable by status and date range, paginated with cursor/limit in
→ nextCursor out.
Pass the previous response's nextCursor back as the cursor query param to get the next page;
omit it for the first page. limit defaults to 20; the maximum is not yet defined (see below).
Open question for the API team: this pagination shape (cursor in, nextCursor out) has no
precedent anywhere in api.taxiid.nl — every existing list endpoint in this workspace uses
LoopBack's ?filter= (skip/limit/where) instead, and /v1/rides itself doesn't exist yet
(integrations.taxiid.nl's /v1/* surface is unbuilt). Confirm whether the gateway will actually
introduce cursor pagination or follow the existing skip/limit convention, and what the real
default/max limit is, before treating this as final.
GET /v1/rides/{id}
Subject: retrieve a single ride's current state.
POST /v1/rides/{id}/offer
Subject: offer a ride to a driver or company for dispatch. driverId is expected to be scoped to
your own company — every existing write endpoint in api.taxiid.nl enforces company ownership
this way (e.g. a pluginInstallId/company id supplied in a request body is always checked against
the caller's own company, 404ing otherwise), and this endpoint is expected to follow the same
convention once built. This specific check has not been implemented yet, so treat it as the
expected behavior rather than a confirmed guarantee until integrations.taxiid.nl's /v1/*
surface actually ships.
Webhook: ride.status_changed
Subject: delivered whenever a ride you created or were offered changes status.
Webhook reliability
Subject: delivery is via the same outbox mechanism used for every other taxiID integration
(Wintax/Taxsys) — confirmed in api.taxiid.nl: 8 attempts by default
(OUTBOX_MAX_ATTEMPTS), exponential backoff starting at 5 seconds and doubling each attempt
(5s, 10s, 20s, 40s, …). After the final attempt the delivery is marked failed (dead-lettered)
and not retried further.
Open question for the API team: no replay/recovery mechanism for a dead-lettered event exists
today — there is no endpoint to re-trigger a missed webhook. Until one exists, the only fallback
is to poll GET /v1/rides/GET /v1/rides/{id} for rides you haven't heard a terminal status for
in a while (that endpoint isn't built yet either, so this is a recommendation, not a guarantee).
Verifying a webhook
Subject: header X-TaxiID-Signature: t=<unix>,v1=<hex hmac> — HMAC-SHA256 over ${t}.${rawBody}
using your webhook secret. Reject stale timestamps.
Open question for the API team: the allowed clock-skew window (how old a t= value may be
before it's rejected as stale) is not yet defined anywhere in api.taxiid.nl or
integrations.taxiid.nl — the signing side (PluginInstallDeliveryService) only signs, it
doesn't itself define a verification tolerance, and the receiving/verification side is unbuilt.
Confirm a specific window (e.g. 5 minutes) before integrators rely on one.
Ride creation
Calls for integrations that book rides on behalf of a passenger or company.
POST /v1/quotes
Subject: get a price quote for a route before booking. Priced by taxiID's pricing service (the
core POST /prices/quotation), which returns every product that can serve the route.
| Field | Type | Required | Description |
|---|---|---|---|
requestedDate |
ISO-8601 datetime | yes | Pickup moment; also used for the fleet-availability check. |
departure / destination |
Address | yes | Send the full address (gps, street, house number, postal code, city, countryCode), not just coordinates. |
stopOvers |
Address[] | optional | Intermediate stops, in order. |
passengerCount / luggageCount |
number | optional | Filter out products whose maxPassengers / maxLuggage is too small. |
vehicleTypes |
string[] | optional | Restrict the quote to these vehicle types. |
isReturnBooking |
boolean | optional | Price a return trip (see also returnRequestedDate). |
Unknown fields are rejected by the pricing service (HTTP 422), so send only the fields above.
The response is an array of priced products; a product whose vehicle type is already full at
requestedDate is still returned but flagged available: false. When nothing can be priced the
call answers 406 with an error body.
Open question for the API team: the exact per-product response fields (product id/name keys,
price breakdown) and which request fields are mandatory are validated by the pricing service
(tps.dispatchapi.io), outside this repo's sources — confirm before relying on them. The core
quotation call is also stateless: the quoteId and expiresAt shown below are what the planned
integrations.taxiid.nl /v1/quotes is expected to add, they do not exist in the core response.
POST /v1/rides
Subject: create a new ride booking. See the Ride object above for what quoteId's
omission means (unconfirmed).
Idempotency — open question for the API team: there is no dedicated Idempotency-Key header
anywhere in this API today. The closest confirmed precedent, from taxiID's other integrations
(Wintax/Taxsys via integrations.taxiid.nl), is deduplication by looking up an existing ride with
the same natural key (Ride.foreignBookingId) before creating a new one — not a header-based
mechanism. Whether POST /v1/rides will apply that same dedup-by-foreignBookingId rule, a real
Idempotency-Key header, or neither, is not yet decided. Until confirmed, do not assume a retry
after a client-side timeout is safe — check GET /v1/rides/{id} (once built) or your own record
of the foreignBookingId you sent before retrying, to avoid double-booking.
POST /v1/rides/{id}/cancel
Subject: cancel an existing ride. Confirmed in api.taxiid.nl: a ride can be cancelled from any
non-terminal status (i.e. anything other than already completed or already
canceled/canceled_driver/canceled_passenger — see the Ride status enum above);
cancelling a terminal ride is rejected as an invalid state transition (409 invalid_state_transition, see Administration → Errors).
A cancellation charge can apply, governed by the company's own configured refund policy (an
allow-list of chargeable cancellation reasons plus fee tiers) — not a fixed platform-wide fee. It
only applies before the ride starts and only if the ride was already paid for.
Open question for the API team: this depends on the caller supplying a cancellation reason,
and on whether the (unbuilt) gateway endpoint accepts a reason field and reports back whether a
charge applied — neither is confirmed yet for the Developer API specifically.
Webhook: ride.created
Subject: delivered when a ride you created is accepted into the system.
Webhook: ride.updated
Subject: delivered on other ride field changes. Off by default — enable it in the portal if you
need it; it is noisier than ride.status_changed.
./generate.sh to rebuild.