FikaChu Docs

API client contract

Current HTTP contracts that matter for third-party integrators.
For a full partner narrative with curl and end-to-end examples, see
FIKACHU_RIDE_BOOKER_API.md (/guides/ride-booker/).
Developer hub: /.

Conventions

Topic Rule
Auth Authorization: Bearer <access_token> from your FikaChu-issued OIDC / OAuth client
Longitude shortform FikaChu JSON uses **lon** (stops, destination_filter, rental vehicle/location, driver location). Rental gps_trail is [[lon, lat], …] pairs. GBFS feeds keep MobilityData lon. GeoJSON centers stay [lon, lat] array order.
Content type JSON unless multipart (KYC, stop verification media)
Soft offers Unaccepted DRIVER_ASSIGNED trips are push/WebSocket only — not in GET /drivers/v1/active
Back-to-back While DRIVING_WITH_CLIENT, matching near dropoff may queue a next_trip on accept (when enabled for the deployment)
Offer timeout Soft offers expire after a short window (typically ~30 seconds); check offer_expires_at when present

2026-08-15 — Ping vehicle proximity (200 m)

Breaking. Both ping-vehicle endpoints require the driver's current coordinates. Ping is refused when farther than 200 m from vehicle.location.

{ "lat": -6.7924, "lon": 39.2083 }
Status code Meaning
400 ping_driver_location_required Missing/invalid lat/lon
400 ping_vehicle_location_unknown Vehicle has no location
400 ping_too_far Also distance_m, max_distance_m (200)

2026-08-15 — Ping vehicle (replaces browse Ring + typed alarmArm)

Breaking for driver / rental clients. Finding a vehicle no longer sends a typed IoT command (alarmArm via commands/send or POST …/rental-vehicles/{id}/ring).

Client Endpoint Body
Browse (no rental) POST /drivers/v1/rental-vehicles/{vehicle_id}/ping-vehicle { "lat", "lon" } (driver GPS; max 200 m)
Active rental POST /ride-booker/v1/rentals/{ride_id}/ping-vehicle { "lat", "lon" } (driver GPS; max 200 m)

Both require feat-iot-alarm and an iot_device_id. Response: { vehicle_id, device_id, iot_response }. There is no command_type field. /ring is removed (404).

The API looks up a saved Traccar command id from device (then group) attribute pingDeviceCommand and POSTs { "id", "deviceId" } to Traccar. Clients must not send alarmArm.

Status Meaning
200 Ping accepted
400 Disabled, missing feat-iot-alarm, rental not reserved/in-progress, pingDeviceCommand not configured, missing GPS, unknown vehicle location, or farther than 200 m (ping_too_far)
404 Vehicle / ride / IoT device missing
502 Traccar lookup or command delivery failed

Show Ring only when feat-iot-alarm is present (unchanged). Map 400 “not configured” and 502 to user copy; do not retry 4xx.


Rental estimation and rental vehicle payloads (nearby / details) expose vehicle_equipment as capability slugs. Clients gate UX as follows:

Slug When present When absent
feat-reserve-on-fikachu In-app Reserve Call to reserve (station contact_phone, else system phone_number)
feat-iot-alarm Show Ring (browse + active rental) Hide Ring
feat-iot-lock IoT lock on pause/end Hide IoT lock; show manual lock instructions
feat-iot-unlock IoT unlock to start Hide IoT unlock; show manual unlock instructions

Also:

  • Estimation vehicle includes station_id (GBFS station id) when docked.
  • Browse Ring (no active rental): POST /drivers/v1/rental-vehicles/{vehicle_id}/ping-vehicle — see 2026-08-15 ping-vehicle. Do not call /ring or commands/send with alarmArm.

2026-08-11 — Onboarding service subset + verified services

Breaking: Required document types resolve from active_service_types only (not supported). Empty active ⇒ no required docs.

Zero-doc auto-verify: After onboarding scope is set (region + vehicle type + active services) with no mandatory DocumentTypes, PATCH /drivers/v1/onboarding syncs is_verified=true and kyc_case_status=approved so the driver can enter the app without submit-for-review. POST /drivers/v1/onboarding/submit also approves immediately when the KYC policy is already met (including the zero-doc case).

PATCH /drivers/v1/onboarding when setting vehicle_type_id must include service_types (slugs/ids ⊆ that vehicle type’s services):

{
  "onboarding_state": { "vehicle_type_id": "<uuid>" },
  "service_types": ["delivery"]
}

Side effects: supported_service_types = all services linked to the vehicle type; active_service_types = the chosen subset.

GET /drivers/v1/onboarding adds supported_service_types / active_service_types (same brief shape as profile).

Profile service brief adds verified (bool): mandatory KYC for that service alone is fully approved (or no mandatory docs).

PATCH /drivers/v1/profile active_service_types: activating an unverified service returns 400 with:

{
  "active_service_types": [
    {
      "code": "service_kyc_incomplete",
      "service": "ridehail",
      "missing_document_types": [{ "id": 1, "slug": "driving_license", "upload_status": { "single": null }, "...": "..." }]
    }
  ]
}

Missing document payloads include upload_status (latest KYC status per side) so clients can show uploaded-but-pending vs not-uploaded. The same error also includes optional_document_types (skippable types for that service) so clients can list them with an Optional tag without blocking activation.

POST /drivers/v1/kyc/submit-review: After a verified driver uploads supplementary docs (activate-service walkthrough), queues the case for ops review:

  • kyc_case_status=approvedneeds_additional_documents
  • kyc_case_status=draftsubmitted

Returns { "kyc_case_status", "onboarding" }.


2026-08-11 — Driver remittance invoices + billing access gate

Breaking invoice shape for ridehail/delivery driver platform fees posted to FikaShop:

Before After
payer = billing partner, payer_wallet = partner wallet payer = driver, payee = partner, use_payer_wallet + use_payee_wallet (optional explicit payee_wallet when partner GET returns wallet_id)
Cash: commission lines only Cash: booking/service fees + tax + commission
Non-cash: commission only Unchanged (fee+tax already on rider invoice)

Driver profile (GET/PATCH /drivers/v1/profile) adds read-only:

"billing_access": {
  "allowed": true,
  "soft_warning": false,
  "owed_amount": 0,
  "currency": "TZS",
  "min_due": 0,
  "due_at": null,
  "unpaid_invoice_ids": [],
  "block_reason": null
}

Going online while hard-gated returns 403 with code: "driver_billing_blocked" and billing_access (both PATCH and PUT). Debt thresholds live on SystemRegion (driver_debt_soft_limit, driver_debt_hard_limit, driver_debt_grace_hours, driver_debt_min_payment_ratio).

Unlock rule: full remittance invoice settlement (driver_fee_payment_status=paid). min_due is advisory for UI only — partial payment does not clear the gate.

Hardening: driver-fee webhooks reject underpaid paid against remittance total (a low payload.total cannot bypass). Remittance create validates FikaShop total/amount_paid. Failed creates without an ext id still count toward owed until ops retries (--missing-driver-invoice-only). Matching also excludes soft past-due drivers via cached billing_due_at / owed even before billing_access_blocked is refreshed.

See OPERATIONS.md for remittance rules, retries (--missing-driver-invoice-only), partner token refresh, and Phase 2 digital setoff notes.


2026-08-10 — ImageUpload galleries + GBFS vehicle_assets removed (breaking)

Breaking feed change: vehicle_types.json (and ride estimation vehicle_type) no longer include vehicle_assets. Use vehicle_image for the identifying photo. Gallery photos are managed via the operator media API (Fleet Manager), not partner Ride Booker calls.

Surface Behavior
vehicle_assets on vehicle type Removed
Linked gallery images on vehicle type / station / pricing plan Via ImageUpload
Vehicle feature image Via ImageUpload
System brand icons Still via brand assets (light/dark)

Operator media API (Fleet Manager; not required for Ride Booker partners):

Method Path Notes
POST /gbfs/v1/images/ multipart file (+ optional caption, content_type, object_id)
GET /gbfs/v1/images/?content_type=&object_id= list linked photos
GET/DELETE /gbfs/v1/images/{id}/ detail / delete
POST /gbfs/v1/images/{id}/attach/ JSON {content_type, object_id}
POST /gbfs/v1/images/{id}/detach/ JSON {content_type, object_id}
POST /gbfs/v1/images/attach/ bulk link: JSON {content_type, object_id, image_ids: [...]}
POST /gbfs/v1/images/detach/ bulk unlink: same body; returns remaining linked photos

Allowlisted content_type values: gbfs.station, gbfs.vehicle_type, gbfs.pricing_plan, gbfs.vehicle_feature.
For gbfs.vehicle_feature (FK), bulk attach requires exactly one image_id.


2026-08-09 — Developer docs URL cutover (breaking for bookmarks)

Breaking path change for curated docs on the API host:

Resource Before After
Developer landing (none) /
Ride Booker partner guide markdown in repo only /guides/ride-booker/
API contract notes markdown in repo only /guides/api-changes/

Interactive OpenAPI (ReDoc, Swagger UI, /swagger.json) is no longer publicly exposed.


2026-08-09 — Driver trip privacy (breaking for driver clients)

Breaking wire change for driver list / detail / active trip payloads:

Field Behavior
rider_name Always first name + last initial (e.g. Jane D.)
rider_phone Present while trip is non-terminal or within ServiceType.driver_trip_contact_grace_hours after terminal (end_time, else last_updated); otherwise null
Stop address / lat / lon Exact while in contact window; after grace: coarsened street/area label and coords rounded to 3 decimal places
Ride / stop notes Cleared ("") after grace
Fare, distance, timestamps, payment Unchanged

Grace is configured per ServiceType.driver_trip_contact_grace_hours (default 48). Missing service type → grace 0 (immediate history redaction).

Cancelled trips: driver app hides name / notes / manifest on trip detail (UI-only in this release; API may still return them).


2026-08-11 — Longitude shortform lon + gps_trail pairs (breaking)

Breaking wire change: FikaChu JSON longitude keys are lon only (no lng alias). Compound shortcuts are pickup_lon / dropoff_lon.

Surface Before After
Ride-booker stops lng required lon required
Driver trip stops[] lng lon
destination_filter {lat, lng, …} {lat, lon, …}
Driver PUT /location, nearby rental query lng lon
Rental vehicle / complete location lng lon
Nested booker vehicle, trip-share vehicle_location lng lon
WS / trip shortcuts pickup_lng / dropoff_lng pickup_lon / dropoff_lon
Rental gps_trail [{lat, lng}, …] objects [[lon, lat], …] pairs (GeoJSON order)

Unchanged: GBFS feed lon, GeoJSON [lon, lat] order, Photon/Valhalla upstream params, promotions ?point=lon,lat.


2026-08-09 — Longitude shortform lng (breaking)

Historical: FikaChu JSON longitude keys were standardized on lng only (no lon alias). Superseded by 2026-08-11 — Longitude shortform lon.

Surface Before After
Ride-booker stops lon preferred; lon input alias lng required
Driver trip stops[] lon lng
destination_filter {lat, lon, …} {lat, lng, …}
Driver PUT /location, nearby rental query lon lng
Rental vehicle / complete location / gps_trail lon lng
Nested booker vehicle, trip-share vehicle_location lon lng

Unchanged at the time: GBFS feed lon, GeoJSON [lon, lat] order, Photon/Valhalla upstream params, pickup_lng / dropoff_lng.


2026-08-09 — Verification barcodes list key (breaking)

Breaking wire change: verification_requirements.barcodes uses nested barcodes instead of items:

"barcodes": {
  "enabled": true,
  "barcodes": [{ "type": "CODE128", "value": "BOX-1" }]
}

Driver-submitted verification.barcodes (scan evidence list) is unchanged.


2026-08-09 — Stop action item_external_ids (breaking)

Breaking wire change: ride-booker create (and any stop actions on estimate) use actions[].item_external_ids instead of actions[].items. Matches create/detail manifest_actions[].item_external_ids.


2026-08-09 — ServiceType.service_flow (breaking for UI clients)

Breaking wire change for driver profile / trip payloads that branch on product UX.

Field Role
slug / service_type Unique API identity (custom names allowed)
service_flow Behavior family: ridehail | delivery | vehicle_rental | rideshare
  • ServiceTypeBrief / vehicle service_types tags: add service_flow
  • Ride booker RideSerializer and driver trip payloads: add service_flow (from service_type.service_flow); service_type remains the slug
  • Bookable create resolves by slug but requires service_flow in {ridehail, delivery, rideshare}
  • Rentals / demand / analytics rental-only branch on service_flow, not slug equality

Clients must key home chrome and trip shells off service_flow, not hardcoded slug lists.


2026-08-09 — ServiceTypeBrief.image

ServiceTypeBrief (profile supported_service_types / active_service_types) adds image: absolute media URL for ServiceType.image, or "" when unset.


Breaking wire change for driver home demand map (same-day cutover from geohash rectangles).

Before After
Geohash precision-6 axis-aligned rectangles; id gh:…; properties.geohash H3 resolution-8 hex polygons; id h3:{cell}; properties.cell
Unbounded feature count in viewport Cap 60 features by pickup_count (busiest cores)
Parent geohash p4 + required viewport bbox Unchanged — still west,south,east,north (max 2°); intensity per parent

Query (unchanged)

GET /drivers/v1/demand-zones?west=39.15&south=-6.85&east=39.25&north=-6.75

Missing/invalid bbox → 400. Nav still uses properties.center [lon, lat].

Live heat FeatureCollection

{
  "type": "FeatureCollection",
  "generated_at": "2026-08-09T09:00:00Z",
  "window_minutes": 60,
  "features": [
    {
      "type": "Feature",
      "id": "h3:882a100d33fffff",
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[39.21, -6.79], [39.215, -6.788], "...", [39.21, -6.79]]]
      },
      "properties": {
        "cell": "882a100d33fffff",
        "pickup_count": 14,
        "intensity": 0.82,
        "center": [39.208, -6.792]
      }
    }
  ]
}
  • Ring coordinates and center are [lon, lat]; ring is closed (first == last).
  • Tessellating hexes — no radius_m, no square geohash boxes.
  • Storage: DemandHotZone.cell_id (H3) + parent_geohash (p4) for regional index.

2026-08-09 — Demand hot zones: geohash index + viewport bbox (superseded)

Superseded by H3 hex + geohash-shard cutover above (same-day). Historical note: viewport bbox + parent geohash p4 indexing replaced the global Redis blob; geometry briefly used geohash-6 rectangles before hex restore.


2026-08-09 — Live demand hot zones → H3 hex polygons (historical)

Earlier same-day note: H3 Polygon cutover from Point + radius_m circles (then briefly geohash rectangles; see current H3 + shard section above).


2026-08-08 — ride_typebooking_type (breaking)

Breaking wire change after the Ride model redesign. Booking category is no longer named ride_type.

Before After Values (unchanged)
ride_type booking_type on_demand | prebooked | flexible

Surfaces

  • Ride-booker ride create/detail/list JSON (RideSerializer)
  • Driver GET /drivers/v1/trips (+ trip detail) (DriverTripSerializer)
  • Soft-offer WebSocket / push meta for rides_ride.driver_offer_ready

Cutover rules

  • No dual-key / legacy alias period: clients must read booking_type only.
  • External ride-booker or webhook consumers that still parse ride_type must switch in the same deploy window.

Unchanged client keys

  • pickup_time (prebooked/flexible)
  • offer_expires_at (soft-offer meta)
  • Rental: is_paused, pause_deadline, parking_photo_url, gps_trail
  • fulfillment_parent_ride_id / parent_ride_id

Ride booker (/ride-booker/v1/)

estimate → fare_id → create for on-demand, prebooked, and flexible. Same envelope on all three.

Estimate

Paths: POST …/rides/estimations, …/rides/prebooked/estimations, …/rides/flexible/estimations.

Request

  • stops[] with lat + **lon** (min 2 for on-demand/prebooked).
  • Optional payment_methods[] (e.g. ["cash", "wallet"]). Omit to use the intersection of rider-allowed methods (default ["cash"] when empty).
  • Prebooked/flexible: optional or required pickup_time as documented on each path.
  • Do not send vehicle_type_id or a single payment_method on estimate.

Response

{
  "categories": [
    {
      "vehicle_type": {
        "id": "<uuid>",
        "name": [{"text": "Economy", "language": "en"}],
        "short_description": [{"text": "…", "language": "en"}],
        "description": [{"text": "…", "language": "en"}],
        "vehicle_assets": {},
        "rider_capacity": 4,
        "propulsion_type": "combustion",
        "cargo_volume_capacity": null,
        "cargo_load_capacity": null,
        "max_range_meters": null,
        "vehicle_image": ""
      },
      "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": "<token>",
          "expires_at": 1730000000,
          "price": {
            "amount": "12500–12500 TZS",
            "minimum_amount": 12500.0,
            "maximum_amount": 12500.0,
            "currency_code": "TZS"
          }
        }
      ]
    }
  ]
}
  • **pickup_estimate**: seconds until nearest eligible driver at pickup; null when no_vehicles_available is true, or for prebooked/flexible (live fleet N/A).
  • **no_vehicles_available**: true when no eligible online vehicles for this type near pickup (on-demand only; always false for prebooked/flexible). Excludes busy drivers and vehicles beyond the match radius / max pickup ETA.
  • **available_vehicle_count**: eligible pool size after busy/radius filters (before routing); 0 for prebooked/flexible.
  • **trip.distance_estimate** / **trip.duration_estimate**: routed pickup→dropoff distance and duration (seconds).
  • **expires_at**: unix timestamp when the fare_id expires (matches fare cache TTL; on-demand ~5 minutes, flexible ~24h).
  • Invalid payment methods for a category are omitted from estimations (not a 400).
  • Flexible uses the same categories envelope (not a separate vehicle_categories shape).

Create

Paths: POST …/rides/create, …/rides/prebooked/create, …/rides/flexible/create.

Request (core)

{
  "fare_id": "<from chosen estimation>",
  "stops": [{ "lat": -6.79, "lon": 39.21 }],
  "user": { "phone": "+255700000000", "name": "Rider Name" },
  "notes": "optional",
  "service_type": "ridehail",
  "manifest_items": []
}
  • vehicle_type_id and payment_method come from the consumed fare_id — do not send them on create.
  • Optional user is display metadata (rider_display_name, rider_contact_phone); the authenticated user remains the ride owner unless a Developer-group foreign phone is used.
  • Ride-level driver message field is **notes** (not note_to_driver).
  • Flexible create: **fare_id required**; stops/notes/pickup_time optional as documented.
  • Prebooked create: scheduled ride; may auto-dispatch into matching when enabled.

Delivery / cargo

Optional on create:

  • service_type: ridehail (default) or delivery.
  • manifest_items[] catalog (external_id, name, quantity, …).
  • Per-stop actions: pickup | dropoff | return, each with item_external_ids referencing catalog external_ids. Quantities must balance across stops.
  • Per-stop verification_requirements — keys only: barcodes, picture, pincode, signature.

Example

{
  "fare_id": "<from estimation>",
  "service_type": "delivery",
  "manifest_items": [
    { "external_id": "BOX-1", "name": "Parcel", "quantity": 1, "is_fragile": true }
  ],
  "stops": [
    {
      "lat": -6.79,
      "lon": 39.21,
      "actions": [{ "type": "pickup", "item_external_ids": ["BOX-1"] }]
    },
    {
      "lat": -6.80,
      "lon": 39.28,
      "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" }]
        }
      }
    }
  ]
}

Barcode types: CODE39, CODE39_FULL_ASCII, CODE128, QR. Unknown requirement keys → 400.

Other booker paths: details, location, receipt, cancel.


Drivers (/drivers/v1/)

Profile preferences (GET|PATCH /drivers/v1/profile)

Writable preference fields (direct cutover; no legacy aliases):

Field Notes
active_service_types Matching allowlist (ids or slugs)
navigation_app / voice_guidance Client nav
avoid_tolls / avoid_highways Passed to OSRM / Google Maps deep links
rest_reminder_hours Client rest banner while continuously online
accepted_payment_methods JSON list; empty = all methods
destination_filter Nested object {lat, lon, address, radius_km, expires_at, one_shot}; null clears. Stored as GIS Point.
auto_accept_offers / auto_accept_max_pickup_eta_seconds Soft-offer auto ON_ROUTE
accept_long_trips / long_trip_min_distance_km Opt out of long crow-flies trips
max_match_radius_km Personal max crow-flies km vehicle→pickup; null = unlimited
active_vehicle When online, only this owned vehicle is online

Matching applies the vehicle type search radius, then filters on payment, destination, long-trip, and the driver's own max_match_radius_km preferences.

Read-only quality scores on profile:

Field Notes
acceptance_rate Percent accepts / (accepts + rejects + expiries); null until decided offers exist
cancellation_rate Percent driver cancels / (completed + driver cancels); null until closed trips exist
quality_score Composite 0–100 from rating, acceptance, and cancellations

Trips list and detail

  • GET /drivers/v1/trips — paginated history; filters: status, category (maps to booking_type, e.g. prebooked), period, from, to.
  • List/detail trip payloads include pickup_time (from RideSchedule; null when absent / on-demand) and tip_amount (from post-trip rating when set).
  • GET /drivers/v1/trips/{ride_id} — detail for the assigned driver (same path as PATCH). Includes list fields plus:
  • pickup_lat / pickup_lon / dropoff_lat / dropoff_lon
  • fare_breakdown (from TripCostBreakdown, or null)
  • rider_name, rider_phone
  • driver_has_rated_rider (ridehail) / platform rating flags as applicable
  • current_stop_index, stops[] (longitude as **lon**), manifests, verification blobs

Active workloads

GET /drivers/v1/active{ "trip": … | null, "next_trip": … | null, "rental": … | null }.

  • trip — current accepted ridehail/delivery (earliest start_time, else created)
  • next_trip — queued back-to-back trip when the driver accepted a second offer while on the current trip (at most one)
  • rental — newest active vehicle rental

Soft offers (DRIVER_ASSIGNED) are excluded until action=accept.

State transitions

PATCH /drivers/v1/trips/{ride_id}

{
  "action": "accept | reject_offer | arrive | driver_arrived | start | arrive_at_stop | complete | cancel | client_did_not_show | commit | decline_schedule",
  "driver_note": "optional",
  "verification": {}
}
Action Notes
accept Commit soft offer → on route; while on DRIVING_WITH_CLIENT, queues as next_trip (max one)
reject_offer Decline soft offer
arrive / driver_arrived Approach / arrived at client
start In progress; marks pickup complete and advances current_stop_index past stop 0 when a next stop exists
arrive_at_stop Mid-stop complete (not final); may require verification / driver_note; then increments index
complete Final stop; may require verification; triggers billing finalization
cancel Driver cancel
client_did_not_show Rider no-show from ARRIVED_AT_CLIENTCLIENT_DID_NOT_SHOW
commit Accept scheduled prebook (SCHEDULED → commit → dispatch → on-demand match)
decline_schedule Decline scheduled prebook (SCHEDULED / DRIVER_COMMITTED → cancelled)

Rate trip

POST /drivers/v1/trips/{ride_id}/rate body: { "score": 1-5, "tags": [...], "tip_amount"?: number }.
Optional tip_amount is stored on RideRating and returned on trip list/detail as tip_amount.

200 includes at least ride_id, status, and usually current_stop_index.

Proof of delivery media

POST /drivers/v1/trips/{ride_id}/stops/{stop_order}/verification-media

  • Multipart: file + kind (picture | signature).
  • Pictures: JPEG/PNG/WebP; signatures: same + SVG.
  • Response includes image_url and merged verification — pass URLs in later PATCH verification.

Example verification evidence:

{
  "pincode": { "value": "4242" },
  "picture": { "image_url": "https://…" },
  "signature": { "image_url": "https://…", "signer_name": "Ada" },
  "barcodes": { "scanned": ["BOX-1"] }
}

Rentals (/ride-booker/v1/rentals/)

Post-trip billing (no fare_id). Use GBFS vehicle_id (public id).

Path Purpose
estimations / create Rate card / start session
{ride_id}/details Detail (GET /ride-booker/v1/rentals/{ride_id} is a compatibility alias)
{ride_id} PATCH Compat action: { "action": "end" } → stop workflow, status: "stopping"
{ride_id}/location, complete, cancel, receipt Lifecycle
{ride_id}/pause, resume, parking-photo, zones Session extras
{ride_id}/commands List IoT command types (owner-scoped)
{ride_id}/commands/send Send typed command: { "type", "attributes"? } (engine lock/unlock, etc.)
{ride_id}/ping-vehicle Find/ring vehicle (saved IoT ping; not alarmArm)

Rental command endpoints require Ride.user == request.user. Prefer these over standalone IoT command when acting inside a rental.


IoT (/iot/v1/)

Path Purpose
POST /iot/v1/command { "deviceId", "type", "attributes"?, "connection_id"? } — owner, driver, or staff
GET /iot/v1/session-token Vendor JWT + connection base_url / upload_url

Prefer rental-scoped command endpoints when acting inside a rental session. Device webhook ingest under /iot/v1/webhooks/… is for device backends, not partner Ride Booker clients.


Billing (/billing/v1/)

Partner-facing surfaces:

  • Trip cost breakdown and dispute endpoints under /billing/v1/.
  • Ride completion produces a receipt available on the Ride Booker receipt paths.

Payment provider callbacks under /billing/v1/webhooks/fikashop* are server-to-server (not partner ride webhooks). Configure webhook URLs and secrets with your FikaChu contact.