FikaChu Ride Booker API
Partner integration guide for booking on-demand, prebooked, and flexible ridehail (and delivery) through FikaChu.
How to use these docs
- Start at the developer hub
/for product cards and getting-started. - Use this guide for auth, flows, curl examples, webhooks, and errors.
- Track wire cutovers in
API_CHANGES.md(/guides/api-changes/).
This document is self-contained for partner integration.
Table of contents
- Introduction
- Getting access
- Environments
- Auth
- Ride types at a glance
- Endpoint reference
- On-demand rides
- Prebooked rides
- Flexible rides
- Delivery use case
- Webhooks
- Errors
- Sandbox checklist
- Related APIs
- FikaChu integration notes
1. Introduction
The Ride Booker API lets third-party platforms (resellers, corporate travel, e-commerce shipping, etc.) create and manage trips on FikaChu without embedding the rider mobile app.
Core pattern for every bookable category:
- Estimate —
POST …/estimationswith stops (and schedule when required) → receivecategories[], each with one or morefare_idvalues. - Create —
POST …/createwith a chosenfare_idand the same stops → receiveride_id. - Track — poll
GET …/details(and on-demandGET …/location), and/or receive webhooks. - Finish —
GET …/receiptafter completion, orPOST …/cancelwhile cancellation is allowed.
fare_id binds vehicle type and payment method. Do not send vehicle_type_id or payment_method on create.
All paths below are relative to your environment base URL (see Environments). Unless noted, every call requires a Bearer access token.
2. Getting access
- Contact your FikaChu representative to enable Ride Booker API access for your organisation.
- Obtain OIDC / OAuth client credentials and the identity issuer URL from your FikaChu contact.
- Confirm which payment methods (
cash,wallet, …) and vehicle categories are enabled for your partnership. - Optionally register HTTPS webhook endpoints your platform can receive (see Webhooks).
Your integration obtains Bearer tokens from that issuer. FikaChu validates them via OAuth2 token introspection and does not mint partner tokens itself.
3. Environments
| Environment | Base URL (example) | Notes |
|---|---|---|
| Production | https://api.example.com/ride-booker/v1/ |
Live trips and billing |
| Staging / sandbox | https://staging.example.com/ride-booker/v1/ |
Non-production fleet and data |
Replace the host with the value your FikaChu contact provides.
| Resource | Path (on API origin, not under /ride-booker/v1/) |
|---|---|
| Developer hub | GET / |
| Ride Booker guide | GET /guides/ride-booker/ |
| API changes | GET /guides/api-changes/ |
| Health | GET /_health |
Example full on-demand estimate URL:
https://api.example.com/ride-booker/v1/rides/estimations
4. Auth
Every Ride Booker request must include:
Authorization: Bearer <access_token>
Content-Type: application/json
4.1 Obtaining a token
Token issuance is performed against the OIDC / OAuth issuer your FikaChu contact provides. A typical client credentials flow looks like:
curl --request POST 'https://oidc.example.com/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=YOUR_CLIENT_ID' \
--data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=openid'
Example successful response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "openid"
}
Exact scope values and token lifetime depend on your issuer configuration. Cache the token and refresh before expiry (or retry once on 401).
4.2 Using the token
curl --request GET \
'https://api.example.com/ride-booker/v1/rides/details?ride_id=550e8400-e29b-41d4-a716-446655440000' \
--header 'Authorization: Bearer YOUR_TOKEN'
Missing, expired, or unintrospectable tokens → 401 Unauthorized.
4.3 Common authentication issues
| Symptom | Likely cause | Fix |
|---|---|---|
401 on all calls |
Missing/expired Bearer token, or issuer not accepted by FikaChu | Refresh token; confirm issuer URL and client credentials with your FikaChu contact |
403 on create with a foreign user.phone |
Caller is not authorised to book for that phone | Use a phone belonging to the authenticated user, or request elevated access |
| Token works on issuer but API rejects it | Audience / client not authorised for this API | Confirm the token subject is allowed on this FikaChu deployment |
5. Ride types at a glance
| On-demand | Prebooked | Flexible | |
|---|---|---|---|
| Use case | Ride / delivery needed now | Scheduled future pickup | Pre-created request; passenger dispatches when ready |
| Estimate | POST /rides/estimations |
POST /rides/prebooked/estimations |
POST /rides/flexible/estimations |
| Create | POST /rides/create |
POST /rides/prebooked/create |
POST /rides/flexible/create |
| Details | GET /rides/details |
GET /rides/prebooked/details |
GET /rides/flexible/details |
| Cancel | POST /rides/cancel |
POST /rides/prebooked/cancel |
POST /rides/flexible/cancel |
| Key from estimate | fare_id |
fare_id |
fare_id (same envelope as on-demand) |
| Fare TTL | 300 seconds (5 minutes), single-use | 300 seconds, single-use | 86 400 seconds (24 hours) for flexible tokens |
| Time constraints | Immediate | pickup_time between 30 minutes and 90 days ahead |
Optional pickup_time / stops |
| Extra | Location + receipt endpoints | Paginated history | Stops optional on create |
Important: After a prebooked or flexible ride is dispatched into the live matching pool, continue tracking with the on-demand details / cancel / receipt paths using the same ride_id.
Lifecycle states (selected)
Values are the string status field on ride payloads (Ride FSM).
On-demand / in-progress
| Status | Meaning |
|---|---|
REQUESTED |
Created, not yet searching |
SEARCHING |
Looking for a driver |
DRIVER_ASSIGNED |
Soft offer to a driver (may expire) |
DRIVER_ON_ROUTE_TO_CLIENT |
Driver accepted / en route to pickup |
ARRIVED_AT_CLIENT |
Driver at pickup |
DRIVING_WITH_CLIENT |
Trip in progress (after start) |
COMPLETED |
Finished successfully |
CANCELLED |
Cancelled (system / partner / driver) |
CLIENT_CANCELLED |
Cancelled by rider |
CLIENT_DID_NOT_SHOW |
No-show |
NO_DRIVER_FOUND |
Matching failed |
PAYMENT_BOOKING_FAILED |
Payment / booking failure |
Prebooked / flexible (scheduled)
| Status | Meaning |
|---|---|
SCHEDULED |
Waiting for pickup window / dispatch |
DRIVER_COMMITTED |
Driver committed ahead of time |
READY_TO_DISPATCH |
Flexible: ready for passenger dispatch |
DISPATCHED |
Handed off to on-demand-style matching |
OTHER |
Unmapped / transitional |
6. Endpoint reference
All paths relative to /ride-booker/v1/.
| Method | Path | Type | Purpose |
|---|---|---|---|
| POST | /rides/estimations |
On-demand | Quote categories × payment methods |
| POST | /rides/create |
On-demand | Create ride using fare_id |
| GET | /rides/details?ride_id= |
On-demand | Ride state, vehicle, stops, costs |
| GET | /rides/location?ride_id= |
On-demand | Live driver/vehicle GPS (active only) |
| GET | /rides/receipt?ride_id= |
On-demand | Itemised cost after completion |
| POST | /rides/cancel |
On-demand | Rider cancel |
| POST | /rides/prebooked/estimations |
Prebooked | Quote with future pickup_time |
| POST | /rides/prebooked/create |
Prebooked | Create scheduled ride |
| GET | /rides/prebooked/details?ride_id= |
Prebooked | Prebooked detail |
| POST | /rides/prebooked/cancel |
Prebooked | Cancel before on-demand handoff |
| GET | /rides/prebooked/history |
Prebooked | Upcoming prebooked rides (paginated) |
| POST | /rides/flexible/estimations |
Flexible | Categories + fare_id tokens |
| POST | /rides/flexible/create |
Flexible | Create flexible ride |
| GET | /rides/flexible/details?ride_id= |
Flexible | Flexible detail |
| POST | /rides/flexible/cancel |
Flexible | Cancel before dispatch |
Coordinate convention
- Fika request/response longitude shortform is
**lon** (stops, nested bookervehicle,driver_location). - GBFS MobilityData feeds still use
lon(separate contract).
Conventions shared by all creates
| Field | Required | Notes |
|---|---|---|
fare_id |
Yes | From estimation; expires; single-use |
stops |
Yes (except flexible may omit) | Min 2 for on-demand/prebooked; first = pickup, last = final drop |
user |
No | { "phone", "name" } display metadata |
notes |
No | Message visible to driver |
service_type |
No | ridehail (default) or delivery |
manifest_items |
No | Cargo catalog (delivery) |
webhook_url |
No | HTTPS URL for status callbacks |
pickup_time |
Prebooked yes; flexible optional | ISO-8601 datetime |
7. On-demand rides
Typical flow: Estimate → Create → Track → Receipt (or Cancel before pickup completion rules apply).
7.1 Estimate
POST /rides/estimations
Notes
stops: minimum 2 waypoints, visited in order.- Optional
payment_methods(e.g.["cash","wallet"]). Omit to use the intersection of rider-allowed methods across priced vehicle types (defaults to["cash"]when empty). - Do not send
vehicle_type_idor a single top-levelpayment_method. - Each category × allowed payment method yields its own
fare_id. etais seconds until nearest eligible driver (or trip routing duration when no online drivers).- Invalid payment methods for a category are omitted from
estimations(not a 400).
Request
{
"stops": [
{ "lat": -6.7924, "lon": 39.2083, "address": "Masaki, Dar es Salaam" },
{ "lat": -6.8161, "lon": 39.2803, "address": "Julius Nyerere Int'l Airport" }
],
"payment_methods": ["cash", "wallet"]
}
curl
curl --request POST 'https://api.example.com/ride-booker/v1/rides/estimations' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"stops": [
{"lat": -6.7924, "lon": 39.2083},
{"lat": -6.8161, "lon": 39.2803}
],
"payment_methods": ["cash"]
}'
Response 200
{
"categories": [
{
"vehicle_type": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": [{"text": "Economy", "language": "en"}],
"short_description": [{"text": "Affordable everyday rides", "language": "en"}],
"description": [{"text": "…", "language": "en"}],
"vehicle_assets": {
"icon_url": "https://www.example.com/assets/icon_car.svg",
"icon_url_dark": "https://www.example.com/assets/icon_car_dark.svg",
"icon_last_modified": "2021-06-15"
},
"rider_capacity": 4,
"propulsion_type": "combustion",
"cargo_volume_capacity": 2000,
"cargo_load_capacity": 1000,
"max_range_meters": 80,
"vehicle_image": "https://www.example.com/assets/icon_car_dark.svg"
},
"pickup_estimate": 240,
"no_vehicles_available": false,
"available_vehicle_count": 3,
"trip": {
"distance_estimate": 5.0,
"distance_unit": "km",
"duration_estimate": 600
},
"estimations": [
{
"payment_method": "cash",
"fare_id": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example-fare-token",
"expires_at": 1730000000,
"price": {
"amount": "12500–12500 TZS",
"minimum_amount": 12500.0,
"maximum_amount": 12500.0,
"currency_code": "TZS"
}
}
]
}
]
}
Response fields
| Field | Meaning | Unit / notes |
|---|---|---|
categories[] |
One quote group per bookable vehicle type | — |
vehicle_type.id |
Vehicle type UUID; bound into fare_id (do not send on create) |
UUID |
vehicle_type.name |
Localized display name | { "text", "language" }[] |
vehicle_type.short_description |
Localized short blurb for list UIs | { "text", "language" }[] |
vehicle_type.description |
Localized longer description | { "text", "language" }[] |
vehicle_type.vehicle_assets |
Icon URLs for light/dark UI | GBFS-style object; icon_last_modified is a date string (YYYY-MM-DD) |
vehicle_type.rider_capacity |
Maximum passengers | count |
vehicle_type.propulsion_type |
Powertrain | GBFS enum (e.g. combustion, electric) |
vehicle_type.cargo_volume_capacity |
Cargo volume when set | liters (nullable) |
vehicle_type.cargo_load_capacity |
Max cargo mass when set | kg (nullable) |
vehicle_type.max_range_meters |
Rated vehicle range when set | meters (nullable) |
vehicle_type.vehicle_image |
Product / hero image | URL string |
pickup_estimate |
ETA for nearest eligible driver to reach pickup | seconds; null when no_vehicles_available or for prebooked/flexible |
no_vehicles_available |
No eligible online fleet for this type near pickup | bool; on-demand only (false on prebooked/flexible) |
available_vehicle_count |
Eligible pool size after busy/radius filters (before routing) | count; 0 on prebooked/flexible |
trip.distance_estimate |
Routed pickup→dropoff distance | number; see distance_unit |
trip.distance_unit |
Unit for distance_estimate |
typically km |
trip.duration_estimate |
Routed trip time pickup→dropoff | seconds |
estimations[] |
One bookable quote per allowed payment method | omitted methods are not bookable for this type |
estimations[].payment_method |
How the rider will pay | e.g. cash,business, wallet |
estimations[].fare_id |
Opaque token for POST …/rides/create |
single-use; invalid after expires_at |
estimations[].expires_at |
When fare_id becomes invalid |
unix seconds (UTC); on-demand ~300s TTL |
estimations[].price.amount |
Display fare band | string including currency code |
estimations[].price.minimum_amount |
Lower fare bound | major currency units |
estimations[].price.maximum_amount |
Upper fare bound | major currency units |
estimations[].price.currency_code |
Fare currency | ISO 4217 (e.g. TZS) |
Store the chosen estimations[].fare_id for create. Prefer expires_at over hard-coding TTL.
7.2 Create
POST /rides/create
Notes
fare_idis mandatory, single-use, and expires after 5 minutes.- Stops should match the estimation route (same coordinates and order).
- Optional
user.phone/user.namefor driver communication; authenticated user remains ride owner unless Developer rules apply. - On success, persist
ride_idfor all subsequent calls.
Request
{
"fare_id": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example-fare-token",
"stops": [
{ "lat": -6.7924, "lon": 39.2083, "address": "Masaki, Dar es Salaam" },
{ "lat": -6.8161, "lon": 39.2803, "address": "JNIA Terminal 3" }
],
"user": { "phone": "+255700000000", "name": "Asha Mwangi" },
"notes": "Please call on arrival",
"webhook_url": "https://partner.example.com/hooks/fikachu-rides"
}
Response 201
{
"ride_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "SEARCHING",
"service_type": "ridehail",
"booking_type": "on_demand",
"stops": [
{
"order": 0,
"lat": -6.7924,
"lon": 39.2083,
"address": "Masaki, Dar es Salaam",
"notes": ""
},
{
"order": 1,
"lat": -6.8161,
"lon": 39.2803,
"address": "JNIA Terminal 3",
"notes": ""
}
],
"estimated_cost": "12500.00",
"currency": "TZS",
"payment_method": "cash",
"notes": "Please call on arrival",
"created": "2026-08-03T12:00:00.000000Z",
"user": { "phone": "+255700000000", "name": "Asha Mwangi" }
}
Error examples
- Invalid / expired / already consumed
fare_id→400{ "detail": "Invalid or expired fare_id. Request a new estimation." } - Foreign phone without Developer group →
403
7.3 Details (track)
GET /rides/details?ride_id=<uuid>
Returns the same RideSerializer shape as create. Poll no more often than about once every 10 seconds unless your agreement says otherwise; prefer webhooks for status fan-out.
curl --request GET \
'https://api.example.com/ride-booker/v1/rides/details?ride_id=550e8400-e29b-41d4-a716-446655440000' \
--header 'Authorization: Bearer YOUR_TOKEN'
When a vehicle is assigned, vehicle may look like:
{
"vehicle_id": "veh_public_123",
"vehicle_type": {
"vehicle_type_id": "…",
"name": "Economy",
"form_factor": "car",
"rider_capacity": 4
},
"lat": -6.795,
"lon": 39.21,
"vehicle_image": null,
"color": "white",
"vehicle_plate_no": "T123ABC"
}
and driver_location:
{ "lat": -6.795, "lon": 39.21 }
7.4 Location
GET /rides/location?ride_id=<uuid>
Lightweight live position for active rides only.
Response 200
{
"lat": -6.8012,
"lon": 39.2145,
"last_updated": "2026-08-03T12:05:01.000000Z"
}
Suitable for map polylines (e.g. up to ~1 Hz). Inactive / wrong status → 400.
7.5 Receipt
GET /rides/receipt?ride_id=<uuid>
Call after COMPLETED. Receipt may appear immediately or shortly after finalization (finalize_pending: true until cost breakdown exists).
Response 200
{
"ride_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 12500.0,
"amount_paid": 0.0,
"amount_due": 12500.0,
"currency_code": "TZS",
"invoice_url": null,
"invoice_uuid": null,
"fare_breakdown": [
{ "type": "ride_fee", "amount": 12000.0 },
{ "type": "booking_fee", "amount": 500.0 }
],
"fare_lines": [],
"finalize_pending": false,
"payment_status": "pending"
}
7.6 Cancel
POST /rides/cancel
Request
{
"ride_id": "550e8400-e29b-41d4-a716-446655440000",
"reason": "Passenger changed plans"
}
Response 200
{
"ride_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "CANCELLED"
}
Cancellation is only allowed in certain in-flight statuses (on-demand: through ARRIVED_AT_CLIENT, not after driving with client / completed). Wrong state → 400 with detail. Rider cancel via this API sets status to **CANCELLED**.
8. Prebooked rides
Same estimate → create pattern with a future pickup_time.
8.1 Estimate
POST /rides/prebooked/estimations
pickup_timerequired: ≥ 30 minutes and ≤ 90 days from now.- Stops: min 2, same rules as on-demand.
- Response envelope identical:
{ "categories": [ … fare_id … ] }. - Fare TTL: 300 seconds.
Request
{
"pickup_time": "2026-08-10T07:30:00+03:00",
"stops": [
{ "lat": -6.7924, "lon": 39.2083 },
{ "lat": -6.8161, "lon": 39.2803 }
],
"payment_methods": ["cash"]
}
Error if pickup_time is out of window → 400 { "detail": "pickup_time must be at least 30 minutes in the future." } (or the max-ahead message).
8.2 Create
POST /rides/prebooked/create
Include the same pickup_time and stops used for the fare. Optional delivery / webhook fields apply as on on-demand create.
Request
{
"fare_id": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example-prebooked-fare",
"pickup_time": "2026-08-10T07:30:00+03:00",
"stops": [
{ "lat": -6.7924, "lon": 39.2083, "address": "Hotel lobby" },
{ "lat": -6.8161, "lon": 39.2803, "address": "JNIA" }
],
"user": { "name": "Asha Mwangi", "phone": "+255700000000" },
"notes": "Flight AB123"
}
Response 201 — same ride shape; typically booking_type: "prebooked", status: "SCHEDULED", pickup_time set.
If server auto-dispatch is enabled, the ride may later move into matching (SEARCHING / on-demand path). After handoff, use on-demand details/cancel/receipt with the same ride_id.
8.3 Details / cancel / history
| Call | Notes |
|---|---|
GET /rides/prebooked/details?ride_id= |
Full ride payload |
POST /rides/prebooked/cancel |
Body { "ride_id", "reason"? } — before on-demand handoff |
GET /rides/prebooked/history?page=1&page_size=20 |
Upcoming rides for the current user |
History response 200 (paginated)
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"ride_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "SCHEDULED",
"service_type": "ridehail",
"booking_type": "prebooked",
"pickup_time": "2026-08-10T04:30:00Z",
"stops": [],
"vehicle": null,
"driver_location": null,
"estimated_cost": "12500.00",
"currency": "TZS",
"total_cost": null,
"payment_method": "cash",
"start_time": null,
"end_time": null,
"notes": "Flight AB123",
"manifest_items": [],
"manifest_actions": [],
"created": "2026-08-03T12:00:00.000000Z",
"user": { "phone": "+255700000000", "name": "Asha Mwangi" }
}
]
}
9. Flexible rides
Flexible rides pre-create a booking token / ride the passenger can use when ready.
9.1 Estimate
POST /rides/flexible/estimations
Stops and pickup_time are optional. Response still uses the shared { "categories": […] } envelope with **fare_id** per vehicle type × payment method (not a separate vehicle-type-only booking field).
Flexible fare tokens use a longer TTL: 86 400 seconds (24 hours).
Request (minimal)
{
"payment_methods": ["cash"]
}
Request (with stops)
{
"stops": [
{ "lat": -6.7924, "lon": 39.2083 },
{ "lat": -6.8161, "lon": 39.2803 }
],
"pickup_time": "2026-08-03T18:00:00+03:00",
"payment_methods": ["cash"]
}
Response — same shape as on-demand estimations.
9.2 Create
POST /rides/flexible/create
fare_id required. Stops optional; if omitted, placeholder coordinates may be used until updated.
Request
{
"fare_id": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example-flexible-fare",
"stops": [
{ "lat": -6.7924, "lon": 39.2083 },
{ "lat": -6.8161, "lon": 39.2803 }
],
"user": { "name": "Asha Mwangi", "phone": "+255700000000" }
}
Response 201 — ride with booking_type: "flexible", often SCHEDULED / READY_TO_DISPATCH depending on configuration.
9.3 Details / cancel
GET /rides/flexible/details?ride_id=POST /rides/flexible/cancelwith{ "ride_id", "reason"? }before on-demand dispatch
After dispatch, use on-demand endpoints with the same ride_id.
10. Delivery use case
Delivery rides use the same estimate/create endpoints. On create, set:
service_type:"delivery"manifest_items[]: cargo catalog (external_idis the stable id)stops[].actions[]:pickup|dropoff|returnwithitem_external_idslisting catalogexternal_ids- Optional
stops[].verification_requirementsfor proof of delivery
Quantities must balance across the trip (pickup / dropoff / return). Unknown requirement keys → 400.
10.1 Create delivery example
Estimate first with the same stop coordinates (actions are not required on estimate). Then:
Request
{
"fare_id": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example-fare-token",
"service_type": "delivery",
"notes": "Handle with care",
"webhook_url": "https://shop.example.com/api/webhook/shipping/",
"manifest_items": [
{
"external_id": "BOX-1",
"name": "Cell phone box",
"quantity": 1,
"weight": 30,
"price": 10000,
"dimensions": { "length": 40, "width": 40, "height": 40 },
"is_fragile": true
},
{
"external_id": "LAP123",
"name": "Laptop",
"quantity": 2,
"weight": 3000,
"price": 12000,
"dimensions": { "length": 30, "width": 10, "height": 20 }
}
],
"stops": [
{
"lat": -6.78652,
"lon": 39.259711,
"address": "Warehouse A",
"notes": "Pickup parcels",
"actions": [
{ "type": "pickup", "item_external_ids": ["BOX-1", "LAP123"] }
]
},
{
"lat": -6.798702,
"lon": 39.255937,
"address": "Customer B",
"notes": "Dropoff phone box",
"actions": [
{ "type": "dropoff", "item_external_ids": ["BOX-1"] }
],
"verification_requirements": {
"pincode": { "enabled": true, "value": "4242" },
"picture": { "enabled": true },
"signature": { "enabled": true, "collect_signer_name": true },
"barcodes": {
"enabled": true,
"barcodes": [{ "type": "CODE128", "value": "BOX-1" }]
}
}
},
{
"lat": -6.78652,
"lon": 39.259711,
"address": "Warehouse A",
"notes": "Return laptops",
"actions": [
{ "type": "return", "item_external_ids": ["LAP123"] }
]
}
],
"user": { "name": "Shop Desk", "phone": "+255711111111" }
}
Response 201 (excerpt)
{
"ride_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "SEARCHING",
"service_type": "delivery",
"booking_type": "on_demand",
"manifest_items": [
{
"external_id": "BOX-1",
"name": "Cell phone box",
"quantity": 1.0,
"weight": 30.0,
"length": 40.0,
"width": 40.0,
"height": 40.0,
"price": 10000.0,
"is_fragile": true,
"must_be_upright": false
}
],
"manifest_actions": [
{
"stop_order": 0,
"action": "pickup",
"item_external_ids": ["BOX-1", "LAP123"]
},
{
"stop_order": 1,
"action": "dropoff",
"item_external_ids": ["BOX-1"]
},
{
"stop_order": 2,
"action": "return",
"item_external_ids": ["LAP123"]
}
],
"stops": [
{
"order": 1,
"lat": -6.798702,
"lon": 39.255937,
"address": "Customer B",
"notes": "Dropoff phone box",
"verification_requirements": {
"pincode": { "enabled": true, "value": "4242" },
"picture": { "enabled": true },
"signature": { "enabled": true, "collect_signer_name": true },
"barcodes": {
"enabled": true,
"barcodes": [{ "type": "CODE128", "value": "BOX-1" }]
}
},
"verification": {}
}
]
}
10.2 Verification requirement keys
Only these top-level keys are allowed:
| Key | When enabled |
|---|---|
pincode |
Requires value (string the driver must match) |
picture |
Driver uploads a photo |
signature |
Optional collect_signer_name, collect_signer_relationship |
barcodes |
barcodes: [{ "type", "value" }] — types: CODE39, CODE39_FULL_ASCII, CODE128, QR |
10.3 Partner expectations during fulfillment
Partners do not upload PoD media on the booker API. Drivers collect evidence via the driver app:
POST /drivers/v1/trips/{ride_id}/stops/{stop_order}/verification-mediaPATCH /drivers/v1/trips/{ride_id}witharrive_at_stop/completeandverification
Partners should:
- Poll
GET /rides/details(or consume webhooks) forstatusand stopverificationblobs. - Treat completed delivery when
statusisCOMPLETEDand required stop verifications are present.
After driver start, FikaChu advances current_stop_index past pickup when a next stop exists (drivers are not asked to re-confirm stop 0).
11. Webhooks
11.1 Registering a URL
Pass webhook_url on create (HTTPS recommended):
"webhook_url": "https://shop.example.com/api/webhook/shipping/"
FikaChu POSTs JSON when ride status events are delivered. Retries: up to 3 attempts with delay on failure.
11.2 Envelope
Outbound body matches the realtime envelope:
{
"schema": "fika.v1",
"type": "ridebooker",
"event_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"event_time": 1722686400,
"event_type": "rides_ride.status_changed",
"meta": {
"ride_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "SEARCHING",
"service_type": "delivery",
"user_id": "42"
}
}
| Field | Description |
|---|---|
schema |
Envelope version (fika.v1) |
type |
Always ridebooker |
event_id |
UUID for idempotent processing |
event_time |
Unix seconds |
event_type |
e.g. rides_ride.status_changed |
meta |
Event payload; includes user_id string and ride fields |
Headers: Content-Type: application/json. Respond with 2xx quickly; slow or non-2xx responses are retried.
11.3 Recommended partner handling
- Deduplicate on
event_id. - Update your order from
meta.status/meta.ride_id. - Optionally confirm with
GET /rides/detailsbefore showing terminal states to end users. - Do not expose your webhook URL publicly without verification (network allowlists, shared secret at your edge, etc.). FikaChu ride webhooks currently POST without a platform HMAC header—protect the endpoint yourself.
12. Errors
12.1 Shapes
Field validation:
{
"stops": ["Ensure this field has at least 2 elements."],
"fare_id": ["This field is required."]
}
Generic:
{ "detail": "Invalid or expired fare_id. Request a new estimation." }
Nested verification errors may appear under keys such as verification_requirements, pincode, barcodes.
12.2 HTTP status codes
| Code | When |
|---|---|
| 200 / 201 | Success |
| 400 | Validation, expired fare, invalid state (cancel / location), bad service_type |
| 401 | Missing or invalid Bearer token |
| 403 | Foreign rider phone without Developer group |
| 404 | Ride not found for this user |
| 5xx | Unexpected server / dependency failure |
12.3 Common issues
| Issue | Guidance |
|---|---|
| Fare rejected on create | Re-estimate; ensure stops and (for prebooked) pickup_time match; create within TTL |
Empty categories |
No priced vehicle types / payment methods for this partnership |
| Cancel rejected | Ride already in a non-cancellable status — check details |
| Location 400 | Ride not in an active status |
| Manifest 400 | Unbalanced pickup/dropoff/return or unknown external_id |
13. Sandbox checklist
Use the staging / sandbox host your FikaChu contact provides:
- Obtain a Bearer token from the issuer.
POST /rides/estimationswith two stops → pick afare_id.POST /rides/createwithin 5 minutes → storeride_id.- Poll
GET /rides/detailsand/or receive a webhook. - Optional: create a delivery with manifest +
verification_requirements+webhook_url. - On completion,
GET /rides/receipt.
14. Related APIs
Not required for core Ride Booker integration; see API_CHANGES.md for details.
| Mount | Audience |
|---|---|
/ride-booker/v1/rentals/ |
Micromobility / vehicle rental partners |
/gbfs/v1/ |
Public GBFS feeds (vehicle discovery; no auth) |
/drivers/v1/ |
Driver enrollment, trip actions, PoD media, nearby rentals, rental-vehicles/{id}/ping-vehicle |
Rental find/ring: POST /ride-booker/v1/rentals/{ride_id}/ping-vehicle (in session) or POST /drivers/v1/rental-vehicles/{vehicle_id}/ping-vehicle (browse). Body { lat, lon }; refused beyond 200 m (ping_too_far). Do not send typed alarmArm. Errors: 400 (not configured / no equipment / too far), 404 (no device), 502 (IoT delivery). See API_CHANGES.md (2026-08-15).
15. FikaChu integration notes
| Topic | FikaChu behavior |
|---|---|
| Create key | Always **fare_id** from categories[].estimations[] (not vehicle type alone) |
| Driver note field | **notes** |
| Longitude | Use **lon** on FikaChu JSON (stops, vehicles on booker/driver/rental APIs). Rental gps_trail is [[lon, lat], …]. |
| Payment on estimate | Optional payment_methods[]; omit for defaults |
| Fare TTL | 300s on-demand/prebooked; 86400s flexible |
| Delivery | First-class via service_type, manifests, verification_requirements |
| Webhooks | Create-time webhook_url; envelope schema / type: ridebooker / event_type / meta |
Support
Include when contacting FikaChu support: environment host, UTC timestamp, ride_id / fare_id (if any), HTTP status, and response detail or validation body.