Rider API Onboarding, Cruz Pass, fare quotes, booking and rider support.
Interactive test console Paste a dedicated development token below. It stays in this browser tab and is sent only to the API endpoint you test.Bearer token
Live Socket events - no polling for rides, notifications or chat Connect once after authentication and keep the connection for the full app session. Socket events are the immediate UI signal; REST is only the recovery path after reconnect.
Step What the app should do When to connect After rider authentication, keep one socket connection alive for the app session. Immediate use Use socket events for matching state, driver updates, trip updates, notifications and chat. Do not poll while connected. Recovery fetch GET /rider/dashboard, GET /rider/notifications, GET /rider/trips/:id/driver-location and the active trip messages endpoint once after reconnect or app resume.Trip room After the rider has a trip, subscribe with socket.emit('trip:subscribe', { tripId }).
Connection pattern
const socket = io(BASE_URL, {
transports: ['websocket'],
auth: { token: accessToken }
});
socket.on('connect', () => setRealtimeConnected(true));
socket.on('disconnect', () => setRealtimeConnected(false));
socket.on('connect_error', showRealtimeError);
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload));
socket.on('${event}', (payload) => onRealtimeEvent('${event}', payload)); Live event contract
Event Important payload Render immediately trip:matching_updatedtripId, matchingState, selectedDriver, acceptancePendingShow searching progress or the selected driver preview immediately. trip:no_driver_foundtripId, matchingStateRender the no-driver-found state without polling. trip:state_updatedtripId, status, trip, driverRefresh the trip state and confirmed driver details from the payload. driver:location_updateddriverId, tripId, locationMove the live driver marker on the map. notification:createdid, tripId, title, body, read, type, createdAtAdd it to the notification list and show an in-app alert. On reconnect, recover with GET /rider/notifications. message:receivedtrip_id, body, created_atAppend the chat message immediately. call:incomingcallId, tripId, status, version, expiresAtOpen the incoming-call screen and offer Accept or Decline. call:state_changedcallId, tripId, status, versionRender CONNECTING or ACTIVE; request your own join token after acceptance. call:endedcallId, tripId, status, endedAt, versionDisconnect LiveKit and close the call screen.
Reconnect recovery
Make the recovery fetch only on the first reconnect after a disconnect or app resume. Merge that server state into local state, then keep rendering live socket events.
Token lifecycle: disconnect on sign-out. If the access token expires, refresh it and reconnect with the new token.
Driver-rider voice calls (LiveKit) Use REST for decisions and recovery, Socket.IO for ringing/state events, and LiveKit for microphone audio. There is no SSE endpoint. Calls are available only when LIVEKIT_ENABLED=true and the trip has the authenticated rider plus its currently assigned driver.
Connection Endpoint / event Frontend action Backend REST POST /api/v1/trips/:id/callsCaller starts; show RINGING until the answer deadline. Send a fresh Idempotency-Key. Socket.IO io(BASE_URL, { auth: { token: accessToken }, transports: ['websocket'] })Connect after login; the server joins your private user room automatically. Do not emit a call-start socket action. Socket.IO call:incomingRecipient opens incoming-call UI and calls accept or decline REST endpoint before expiresAt. Socket.IO call:state_changedOn CONNECTING, both apps request their own join-token. On ACTIVE, show the in-call state. LiveKit WebRTC room.connect(data.serverUrl, data.token)Connect directly to LiveKit and enable microphone; use LiveKit connection/track events for actual audio status, mute and speaker controls. Socket.IO call:endedStop microphone, disconnect the LiveKit room and close the call UI. REST recovery GET /api/v1/trips/:id/calls/activeFetch after reconnect/app resume; a null response means no active call.
Payload: events carry { callId, tripId, status, version, callerUserId, recipientUserId, expiresAt, acceptedAt, activeAt, endedAt, createdAt } where IDs are public-prefixed. Ignore events with an older version for the same call. The join-token response alone contains serverUrl, token, identity and expiresAt; never log or persist the token.
Ringing deadline: the backend uses a 120-second default. The caller may request ringTimeoutSeconds from 10 to 300 when starting a call; render the authoritative backend-provided expiresAt countdown. The frontend cannot extend an already-created call.
Flow: start → call:incoming → accept → call:state_changed CONNECTING → both join-token requests → both LiveKit connects → verified webhook sets ACTIVE → either end → call:ended. Decline, the expiresAt timeout, a terminal trip or 60-minute maximum closes the call. A backgrounded app needs registered Expo push notifications plus a recovery GET when it opens; a socket alone cannot wake it reliably.
Setup: mobile apps install their LiveKit platform SDK, request microphone permission, configure native audio routing/background behavior and keep the token only in memory. Backend needs LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET, an applied Prisma migration, and the signed LiveKit webhook configured at /api/v1/livekit/webhook. Server credentials never go into the app.
Frontend integration guide Base URL: use the environment-specific backend URL. The mobile app should never call the Cruz Pass Supabase database directly.
Authentication: onboarding OTP verification returns an onboarding token. After profile completion, persist the access token in secure device storage and send it as Authorization: Bearer <token>. Refresh tokens must also stay in secure storage.
Booking order: request location permission, resolve pickup and destination coordinates, call POST /api/v1/rider/quotes, show the returned server price, then call POST /api/v1/rider/trips with the quote ID and optional driverAcceptanceTimeoutSeconds. It accepts 10-300 seconds and defaults to 60 when omitted. The value is fixed on that trip and applies to every manual driver offer, automatic move to the next driver, and later matching retry. Never calculate or trust the fare in the app.
Cancel or restart: if a rider already has an active trip, use POST /api/v1/rider/trips/:id/cancel to cancel it, or POST /api/v1/rider/trips/:id/matching/cancel to pause matching while retaining the trip. Retry is allowed only after a session has ended or been cancelled; honour retryAvailableAt when supplied.
Cruz Pass: call GET /passes/active to display balanceNaira. A ride can use at most NGN 1,000 even if the card balance is higher. The backend owns balance decrement, restoration on cancellation, and source synchronisation.
Safe retries: send a fresh UUID in Idempotency-Key for every new mutation. Reuse the same key only when retrying the exact same request. Disable buttons while a request is pending and treat 409 duplicate/request-in-progress responses as non-fatal UI states.
Live activity: connect Socket.IO with auth.token = accessToken. Handle trip:matching_updated, trip:no_driver_found, trip:state_updated, driver:location_updated, notification:created and message:received. Use socket events for immediate UI, but refetch the dashboard, notifications and active-trip messages after reconnecting because REST remains the source of truth.
State rendering: use the trip status returned by the API as the source of truth. Subscribe to socket events when available, but refetch the dashboard after reconnecting or when the app resumes.
Errors: read the stable code and user-safe message fields. Do not display stack traces or raw provider errors.
Minimal booking example
const quote = await api.post('/api/v1/rider/quotes', quoteInput, { idempotencyKey: crypto.randomUUID() });
const trip = await api.post('/api/v1/rider/trips', { quoteId: quote.data.id, driverAcceptanceTimeoutSeconds: 60 }, { idempotencyKey: crypto.randomUUID() }); REST response contract Every response is JSON: { success, data, error }. When success is true, read data. When it is false, use error.code for app logic and error.message for safe UI text.
HTTP Meaning Frontend action 200Request succeeded. Render data. 201Resource created. Persist its returned ID/state. 202Accepted for asynchronous work. Show pending; it is not authentication. 400Invalid request or business rule. Show error.message and field details. 401Token missing, expired or invalid. Refresh session or sign in. 403Access denied. Do not retry. 404Unavailable or hidden resource. Return safely and refresh state. 409State conflict/duplicate/expired resource. Refetch; do not blindly retry. 422Semantically invalid input. Highlight the field. 429Rate limited. Back off, respecting Retry-After. 500Unexpected server failure. Offer a safe retry.
Stable codes: VALIDATION_ERROR, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, TRIP_ACCESS_DENIED, RIDER_ACTION_RATE_LIMITED and OTP_RATE_LIMITED. Each endpoint guide adds any relevant business codes.
Capability status and endpoint map This table is the source of truth for frontend planning. Live means the endpoint is implemented and testable. Partial means only the first phase exists. Planned is a documented contract, not a live endpoint yet.
Capability Status Frontend endpoint(s)
Fare quote, distance, ETA and service area Partial POST /api/v1/rider/quotes; provider-backed geocoding/routing still pending
Driver offers, accept/decline, timeout, reassignment Partial POST /api/v1/trips/:id/dispatch; offer lifecycle endpoints pending
Live driver location and socket recovery Partial POST /api/v1/driver/location; reconnect/replay contract pending
Pickup PIN or QR confirmation Planned POST /api/v1/trips/:id/pickup/verify
Cancellation, no-show fees and preview Partial POST /api/v1/rider/trips/:id/cancel; fee preview/rules pending
Checkout, webhooks, receipts, refunds, disputes Partial /api/v1/payments/intents; receipt/refund/dispute surface pending
Trip history, invoices and saved places Partial POST /api/v1/rider/places; history/invoice GET endpoints pending
SOS, trusted contacts, trip sharing and escalation Planned /api/v1/rider/safety/*
In-trip LiveKit voice calling Live backend; mobile SDK pending /api/v1/trips/:id/calls; requires enabled LiveKit credentials and deployed migration
Driver/vehicle details, blocking and extra-money reports Partial GET /api/v1/driver/me, POST /trips/:id/reports
Support tickets, lost items, attachments and ops queue Partial POST /api/v1/rider/trips/:id/reports; ticket/attachment endpoints pending
Fraud, velocity, payment risk and route anomaly controls Planned /api/v1/risk/* is backend-internal; frontend receives safe challenge/decline codes
POST /api/v1/auth/refresh
Public
Refresh and rotate an access session
Endpoint guide What to send, parse and expect Why it exists Rotates an authenticated rider session after an access token expires.
Before you call Read the refresh token from secure device storage. Never send it to ordinary rider endpoints.
Request fields refreshToken is the opaque token returned by onboarding completion or a previous refresh.
What to parse Replace both stored tokens. Retry the original request once, then sign out if refresh fails.
Expected result 200 returns a new accessToken, refreshToken and expiry.
Important Refresh tokens are rotated and the previous token is revoked server-side.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"refreshToken":"..."})
});
const result = await response.json(); Python payload = json.loads(r'''{"refreshToken":"..."}''')
response = requests.post(BASE_URL + '/api/v1/auth/refresh',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"refreshToken":"..."}
JSON;
$ch = curl_init($baseUrl . '/api/v1/auth/refresh');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/auth/refresh' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"refreshToken":"..."}'
{
"success": true,
"data": {
"accessToken": "<access-token>",
"refreshToken": "<refresh-token>",
"expiresInSeconds": 86400
},
"error": null
}
POST /api/v1/rider/auth/otp/request
Public
Request OTP for existing-rider sign in
Endpoint guide What to send, parse and expect Why it exists Starts OTP sign-in for an existing rider who lost local app credentials.
Before you call Use this for sign-in, not account creation. The phone must already belong to a rider.
Request fields phone is an E.164 Nigerian mobile number.
What to parse Show the masked destination and countdown, then collect the six-digit code.
Expected result 202 accepts the sign-in challenge.
Important Live endpoint. It never creates a new account; unknown phone numbers return RIDER_NOT_FOUND.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/auth/otp/request', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"phone":"+2349054568692"})
});
const result = await response.json(); Python payload = json.loads(r'''{"phone":"+2349054568692"}''')
response = requests.post(BASE_URL + '/api/v1/rider/auth/otp/request',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"phone":"+2349054568692"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/auth/otp/request');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/auth/otp/request' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"phone":"+2349054568692"}'
{
"success": true,
"data": {
"accepted": true,
"channel": "sms",
"destination": "+234 *** *** 692",
"expiresInSeconds": 600
},
"error": null
}
POST /api/v1/rider/auth/otp/verify
Public
Verify existing-rider sign in OTP
Endpoint guide What to send, parse and expect Why it exists Verifies an existing rider sign-in OTP and issues an authenticated session.
Before you call Call the rider auth OTP request first.
Request fields phone and six-digit code are required.
What to parse Store accessToken and refreshToken in secure storage; do not create onboarding profile data.
Expected result 200 returns a rider session.
Important Live endpoint. It authenticates an existing rider and cannot create a profile.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/auth/otp/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"phone":"+2349054568692","code":"123456"})
});
const result = await response.json(); Python payload = json.loads(r'''{"phone":"+2349054568692","code":"123456"}''')
response = requests.post(BASE_URL + '/api/v1/rider/auth/otp/verify',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"phone":"+2349054568692","code":"123456"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/auth/otp/verify');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/auth/otp/verify' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"phone":"+2349054568692","code":"123456"}'
{
"success": true,
"data": {
"rider": {
"id": "RDR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"firstName": "Ada",
"lastName": "Okafor"
},
"accessToken": "<access-token>",
"refreshToken": "<refresh-token>"
},
"error": null
}
POST /api/v1/rider/profile/phone/change/request
Bearer token
Request authenticated phone change (planned)
Endpoint guide What to send, parse and expect Why it exists Starts a verified phone-number change for an authenticated rider.
Before you call Require the current access token and confirm the rider intends to change their number.
Request fields phone is the new E.164 number.
What to parse Show the masked new destination and countdown.
Expected result 202 accepts the phone-change challenge.
Important This endpoint is planned; the old phone remains authoritative until verification succeeds.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/profile/phone/change/request', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"phone":"+2349054568692"})
});
const result = await response.json(); Python payload = json.loads(r'''{"phone":"+2349054568692"}''')
response = requests.post(BASE_URL + '/api/v1/rider/profile/phone/change/request',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"phone":"+2349054568692"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/profile/phone/change/request');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/profile/phone/change/request' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"phone":"+2349054568692"}'
{
"success": true,
"data": {
"accepted": true,
"destination": "+234 *** *** 692",
"expiresInSeconds": 600
},
"error": null
}
POST /api/v1/rider/profile/phone/change/verify
Bearer token
Verify authenticated phone change (planned)
Endpoint guide What to send, parse and expect Why it exists Completes a phone-number change after verifying the new phone.
Before you call Use the authenticated rider session and the challenge code.
Request fields phone and six-digit code are required.
What to parse Refresh the dashboard/session identity after success.
Expected result 200 confirms the changed phone.
Important This endpoint is planned; never update the phone locally before this response.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/profile/phone/change/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"phone":"+2349054568692","code":"123456"})
});
const result = await response.json(); Python payload = json.loads(r'''{"phone":"+2349054568692","code":"123456"}''')
response = requests.post(BASE_URL + '/api/v1/rider/profile/phone/change/verify',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"phone":"+2349054568692","code":"123456"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/profile/phone/change/verify');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/profile/phone/change/verify' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"phone":"+2349054568692","code":"123456"}'
{
"success": true,
"data": {
"phone": "+2349054568692",
"changed": true
},
"error": null
}
POST /api/v1/rider/onboarding/otp/request
Public
Request rider phone OTP
Endpoint guide What to send, parse and expect Why it exists Starts phone-number verification before any rider record or session is created.
Before you call Normalize the phone to E.164, for example +2349054568692. This is public; do not send a bearer token.
Request fields phone is the full international mobile number. The playground adds idempotencyKey automatically.
What to parse Store expiresInSeconds to drive the OTP countdown and display destination only in its masked form.
Expected result 202 Accepted means a challenge was created; it does not mean the rider is authenticated.
Important The OTP is sent by SMS in production. It is never part of a normal API response.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/onboarding/otp/request', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"phone":"+2349054568692"})
});
const result = await response.json(); Python payload = json.loads(r'''{"phone":"+2349054568692"}''')
response = requests.post(BASE_URL + '/api/v1/rider/onboarding/otp/request',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"phone":"+2349054568692"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/otp/request');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/onboarding/otp/request' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"phone":"+2349054568692"}'
{
"success": true,
"data": {
"accepted": true,
"destination": "+234 *** *** 692",
"expiresInSeconds": 600
},
"error": null
}
POST /api/v1/rider/onboarding/otp/verify
Public
Verify OTP and receive onboarding token
Endpoint guide What to send, parse and expect Why it exists Validates the six-digit SMS code and creates a short-lived onboarding session.
Before you call Call OTP request first. Enter the exact phone and six-digit code. This is still public.
Request fields phone and code are required. The playground automatically saves data.onboardingToken into the Onboarding token field on success.
What to parse Read data.onboardingToken. It is an opaque value used only in later onboarding JSON bodies, not in Authorization.
Expected result 200 returns the token and the next onboarding step.
Important Do not persist this token after account completion; it expires and is not a bearer access token.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/onboarding/otp/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"phone":"+2349054568692","code":"123456"})
});
const result = await response.json(); Python payload = json.loads(r'''{"phone":"+2349054568692","code":"123456"}''')
response = requests.post(BASE_URL + '/api/v1/rider/onboarding/otp/verify',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"phone":"+2349054568692","code":"123456"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/otp/verify');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/onboarding/otp/verify' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"phone":"+2349054568692","code":"123456"}'
{
"success": true,
"data": {
"onboardingToken": "<onboarding-token>",
"nextStep": "PROFILE"
},
"error": null
}
POST /api/v1/rider/onboarding/status
Public
Resume onboarding and read its current step
Endpoint guide What to send, parse and expect Why it exists Lets the app resume an interrupted signup at the correct screen.
Before you call Verify the OTP first or paste the raw onboarding token in the Onboarding token field above.
Request fields onboardingToken is injected automatically by this page. Mobile clients put it in JSON exactly as shown.
What to parse Read the completed step flags and route the rider to the first incomplete screen.
Expected result 200 returns the current onboarding state.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/onboarding/status', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"onboardingToken":"..."})
});
const result = await response.json(); Python payload = json.loads(r'''{"onboardingToken":"..."}''')
response = requests.post(BASE_URL + '/api/v1/rider/onboarding/status',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"onboardingToken":"..."}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/status');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/onboarding/status' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"onboardingToken":"..."}'
{
"success": true,
"data": {
"onboardingToken": "<onboarding-token>",
"steps": {
"otpVerified": true,
"profileSaved": false,
"locationConsentRecorded": false,
"profilePhotoUploaded": false
},
"nextStep": "PROFILE"
},
"error": null
}
PATCH /api/v1/rider/onboarding/profile
Public
Save rider profile
Endpoint guide What to send, parse and expect Why it exists Stores the legal profile details used to create the rider account.
Before you call A valid onboarding token is required. Ask for consent before sending personal data.
Request fields firstName, lastName, email, gender and birthDate use ISO YYYY-MM-DD. onboardingToken is auto-injected here.
What to parse Use returned profile/onboarding state to decide whether to request location or a photo next.
Expected result 200 confirms the profile was saved.
Important Do not use display names in place of legal name fields or trust client-side age checks.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/onboarding/profile', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"onboardingToken":"...","firstName":"Ada","lastName":"Okafor","email":"[email protected] ","gender":"FEMALE","birthDate":"1995-08-18"})
});
const result = await response.json(); Python payload = json.loads(r'''{"onboardingToken":"...","firstName":"Ada","lastName":"Okafor","email":"[email protected] ","gender":"FEMALE","birthDate":"1995-08-18"}''')
response = requests.patch(BASE_URL + '/api/v1/rider/onboarding/profile',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"onboardingToken":"...","firstName":"Ada","lastName":"Okafor","email":"[email protected] ","gender":"FEMALE","birthDate":"1995-08-18"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/profile');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X PATCH '<base-url>/api/v1/rider/onboarding/profile' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"onboardingToken":"...","firstName":"Ada","lastName":"Okafor","email":"[email protected] ","gender":"FEMALE","birthDate":"1995-08-18"}'
{
"success": true,
"data": {
"saved": true,
"nextStep": "LOCATION_CONSENT"
},
"error": null
}
PATCH /api/v1/rider/onboarding/location-consent
Public
Record the rider location permission decision
Endpoint guide What to send, parse and expect Why it exists Records the rider decision from the mobile operating-system location prompt.
Before you call Request native OS permission first; call this endpoint with the actual result, including false if declined.
Request fields locationConsent is a boolean. onboardingToken is auto-injected.
What to parse Read the saved decision; location denial must not be treated as an authentication failure.
Expected result 200 confirms the choice.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/onboarding/location-consent', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"onboardingToken":"...","locationConsent":true})
});
const result = await response.json(); Python payload = json.loads(r'''{"onboardingToken":"...","locationConsent":true}''')
response = requests.patch(BASE_URL + '/api/v1/rider/onboarding/location-consent',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"onboardingToken":"...","locationConsent":true}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/location-consent');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X PATCH '<base-url>/api/v1/rider/onboarding/location-consent' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"onboardingToken":"...","locationConsent":true}'
{
"success": true,
"data": {
"locationConsent": true,
"nextStep": "PROFILE_PHOTO"
},
"error": null
}
POST /api/v1/rider/onboarding/profile-photo
Public
Upload a validated profile image (multipart form field: file)
Endpoint guide What to send, parse and expect Why it exists Uploads and validates the rider profile image for account recognition and safety.
Before you call Choose a JPEG, PNG or WebP image using the Profile image chooser below. Do not base64-encode it.
Request fields This is multipart/form-data, not JSON. The browser sends the selected file in field file and sends onboardingToken as a form field automatically.
What to parse Read the returned photo URL/key and display only the approved server result.
Expected result 200 confirms the uploaded photo.
Important The backend validates file type and size; the frontend should also show an upload-progress and retry state.
Request structure and code samples Node.js · Python · PHP · cURL This endpoint uses multipart/form-data . Send the selected image using the exact field name file; do not manually set the multipart Content-Type boundary.
Node.js const form = new FormData();
form.append('onboardingToken', '<onboarding-token>');
form.append('file', fileInput.files[0]);
const response = await fetch('/api/v1/rider/onboarding/profile-photo', { method: 'POST', body: form });
const result = await response.json(); Python with open('profile.jpg', 'rb') as photo:
response = requests.post(BASE_URL + '/api/v1/rider/onboarding/profile-photo',
data={'onboardingToken': '<onboarding-token>'},
files={'file': ('profile.jpg', photo, 'image/jpeg')})
result = response.json() PHP $payload = [
'onboardingToken' => '<onboarding-token>',
'file' => new CURLFile('/path/to/profile.jpg', 'image/jpeg'),
];
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/profile-photo');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_RETURNTRANSFER => true]); cURL curl -X POST '<base-url>/api/v1/rider/onboarding/profile-photo' \
-F 'onboardingToken=<onboarding-token>' \
-F 'file=@/path/to/profile.jpg;type=image/jpeg'
{
"success": true,
"data": {
"uploaded": true,
"profileImageKey": "riders/profile-photo.jpg"
},
"error": null
}
POST /api/v1/rider/onboarding/complete
Public
Create account and issue tokens
Endpoint guide What to send, parse and expect Why it exists Creates the rider account and exchanges the onboarding session for authenticated tokens.
Before you call Complete all required onboarding steps, then call once. Do not call repeatedly from double taps.
Request fields onboardingToken is injected automatically. No bearer token is used for this call.
What to parse Persist data.accessToken and refresh token only in secure mobile storage. This page automatically places accessToken in the Access token field.
Expected result 201 Created means the rider is now authenticated.
Important Use the raw access token in the page field; the playground adds Authorization: Bearer automatically.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/onboarding/complete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"onboardingToken":"..."})
});
const result = await response.json(); Python payload = json.loads(r'''{"onboardingToken":"..."}''')
response = requests.post(BASE_URL + '/api/v1/rider/onboarding/complete',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"onboardingToken":"..."}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/onboarding/complete');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/onboarding/complete' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"onboardingToken":"..."}'
{
"success": true,
"data": {
"rider": {
"id": "RDR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"firstName": "Ada",
"lastName": "Okafor"
},
"accessToken": "<access-token>",
"refreshToken": "<refresh-token>"
},
"error": null
}
GET /api/v1/rider/dashboard
Bearer token
Rider dashboard, active trip, saved places and Cruz Pass
Endpoint guide What to send, parse and expect Why it exists Provides the authenticated home-screen snapshot: rider state, active ride, saved places and pass summary.
Before you call Complete onboarding and paste the raw access token once in the Access token field above.
Request fields No JSON body. The page automatically sends Authorization: Bearer <accessToken>.
What to parse Render the returned active-trip status as the source of truth; refetch after app resume or socket reconnect.
Expected result 200 returns the current dashboard model.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/dashboard', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/dashboard',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/dashboard');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/dashboard' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"activeTrip": {
"id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "SEARCHING",
"pickup": {
"label": "Pickup",
"latitude": 4.8156,
"longitude": 7.0498
},
"dropoff": {
"label": "Destination",
"latitude": 4.83,
"longitude": 7.02
},
"estimatedFareMinor": 201000,
"passDiscountMinor": 0,
"finalFareMinor": null,
"currency": "NGN",
"requestedAt": "2026-08-28T14:22:29.895Z",
"tripVersion": 1,
"driverAcceptanceTimeoutSeconds": 60
},
"recentTrips": [],
"savedPlaces": [],
"cruzPass": null
},
"error": null
}
GET /api/v1/rider/profile
Bearer token
Read the authenticated rider profile
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/profile', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/profile',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/profile');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/profile' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "32f265f3-efee-4bbc-86d5-ed50c21c0267",
"firstName": "Ada",
"lastName": "Okafor",
"email": "[email protected] ",
"phone": "+2349054568692",
"gender": "FEMALE",
"birthDate": "1995-08-18",
"profileImageKey": null,
"memberSince": "2026-08-28T12:00:00.000Z"
},
"error": null
}
PATCH /api/v1/rider/profile
Bearer token
Update editable rider profile fields
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/profile', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"firstName":"Ada","lastName":"Okafor","email":"[email protected] "})
});
const result = await response.json(); Python payload = json.loads(r'''{"firstName":"Ada","lastName":"Okafor","email":"[email protected] "}''')
response = requests.patch(BASE_URL + '/api/v1/rider/profile',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"firstName":"Ada","lastName":"Okafor","email":"[email protected] "}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/profile');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X PATCH '<base-url>/api/v1/rider/profile' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"firstName":"Ada","lastName":"Okafor","email":"[email protected] "}'
{
"success": true,
"data": {
"id": "32f265f3-efee-4bbc-86d5-ed50c21c0267",
"firstName": "Ada",
"lastName": "Okafor",
"email": "[email protected] ",
"phone": "+2349054568692",
"gender": "FEMALE",
"birthDate": "1995-08-18",
"profileImageKey": null
},
"error": null
}
GET /api/v1/rider/notifications
Bearer token
List rider notifications
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/notifications', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/notifications',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/notifications');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/notifications' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "notif-1",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"title": "Driver arrived",
"body": "Your driver has arrived at the pickup point.",
"read": false,
"type": "TRIP_DRIVER_ARRIVED",
"createdAt": "2026-08-28T14:22:31.000Z"
}
],
"error": null
}
POST /api/v1/rider/notifications/:id/read
Bearer token
Mark one notification as read
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/notifications/:id/read', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/rider/notifications/:id/read',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/notifications/:id/read');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/notifications/:id/read' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "notif-1",
"read": true
},
"error": null
}
GET /api/v1/rider/devices
Bearer token
List registered rider notification devices
Endpoint guide What to send, parse and expect Why it exists Registers each authenticated rider installation for Expo device alerts while preserving Socket.IO and REST recovery.
Before you call Request notification permission and obtain an Expo push token after sign-in. Register again whenever the token changes.
Request fields PUT accepts deviceId, platform, expoPushToken, expoProjectId and notificationsEnabled. GET returns safe metadata only; DELETE revokes one registration.
What to parse Push is for background/closed app alerts. In the foreground, use notification:created for the in-app banner. On push tap, fetch the trip and notifications from REST.
Important Push tokens are never returned. A rider can list, update or delete only their own devices.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/devices', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/devices',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/devices');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/devices' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "device-1",
"device_id": "ios-installation-id",
"platform": "IOS",
"expo_project_id": "00000000-0000-4000-8000-000000000000",
"notifications_enabled": true
}
],
"error": null
}
PUT /api/v1/rider/devices
Bearer token
Register or refresh an Expo push device
Endpoint guide What to send, parse and expect Why it exists Registers each authenticated rider installation for Expo device alerts while preserving Socket.IO and REST recovery.
Before you call Request notification permission and obtain an Expo push token after sign-in. Register again whenever the token changes.
Request fields PUT accepts deviceId, platform, expoPushToken, expoProjectId and notificationsEnabled. GET returns safe metadata only; DELETE revokes one registration.
What to parse Push is for background/closed app alerts. In the foreground, use notification:created for the in-app banner. On push tap, fetch the trip and notifications from REST.
Important Push tokens are never returned. A rider can list, update or delete only their own devices.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/devices', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"deviceId":"ios-installation-id","platform":"IOS","expoPushToken":"ExponentPushToken[...]","expoProjectId":"<eas-project-uuid>","notificationsEnabled":true})
});
const result = await response.json(); Python payload = json.loads(r'''{"deviceId":"ios-installation-id","platform":"IOS","expoPushToken":"ExponentPushToken[...]","expoProjectId":"<eas-project-uuid>","notificationsEnabled":true}''')
response = requests.put(BASE_URL + '/api/v1/rider/devices',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"deviceId":"ios-installation-id","platform":"IOS","expoPushToken":"ExponentPushToken[...]","expoProjectId":"<eas-project-uuid>","notificationsEnabled":true}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/devices');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X PUT '<base-url>/api/v1/rider/devices' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"deviceId":"ios-installation-id","platform":"IOS","expoPushToken":"ExponentPushToken[...]","expoProjectId":"<eas-project-uuid>","notificationsEnabled":true}'
{
"success": true,
"data": {
"id": "device-1",
"device_id": "ios-installation-id",
"platform": "IOS",
"notifications_enabled": true
},
"error": null
}
DELETE /api/v1/rider/devices/:id
Bearer token
Remove one rider notification device
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/devices/:id', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.delete(BASE_URL + '/api/v1/rider/devices/:id',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/devices/:id');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X DELETE '<base-url>/api/v1/rider/devices/:id' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "device-1",
"removed": true
},
"error": null
}
GET /api/v1/rider/referral
Bearer token
Read the rider referral code and share payload
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/referral', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/referral',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/referral');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/referral' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"code": "CRUZ-32F265F3",
"shareText": "Join ASTRA Ride with my referral code CRUZ-32F265F3",
"sharePath": "/rider/referral/CRUZ-32F265F3",
"rewards": {
"inviter": 0,
"invitee": 0,
"currency": "NGN"
}
},
"error": null
}
GET /api/v1/rider/legal/terms
Bearer token
Read the current terms and conditions version
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/legal/terms', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/legal/terms',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/legal/terms');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/legal/terms' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"version": "2026-05-12",
"updatedAt": "2026-05-12T00:00:00.000Z",
"acceptanceRequired": true,
"sections": [
"Eligibility",
"Usage rights",
"Safety",
"Payments",
"Privacy"
]
},
"error": null
}
GET /api/v1/rider/passes/active
Bearer token
Read the authenticated rider Cruz Pass balance
Endpoint guide What to send, parse and expect Why it exists Returns the rider Cruz Pass balance and whether it can be applied to a quote.
Before you call Requires an authenticated rider. Sync once after account creation if an external pass may exist.
Request fields No body. Access token only.
What to parse Use balanceNaira for display. The maximum discount per eligible ride is NGN 1,000 even when balance is higher.
Expected result 200 returns a zero balance when no linked pass exists.
Important Never reduce balance in the mobile app; the server owns all debits and reversals.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/passes/active', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/passes/active',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/passes/active');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/passes/active' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": null,
"balanceMinor": 0,
"balanceNaira": 0,
"currency": "NGN",
"maximumRideDiscountMinor": 100000,
"maximumRideDiscountNaira": 1000,
"expiresAt": null
},
"error": null
}
POST /api/v1/rider/passes/sync
Bearer token
Sync eligible Cruz Pass credit from the source database
Endpoint guide What to send, parse and expect Why it exists Looks up an eligible Cruz Pass in the server-side source database using the rider identity and credits it safely.
Before you call Requires an authenticated rider. Call sparingly, such as after onboarding or a manual refresh.
Request fields No business fields are needed. The playground adds an idempotency key.
What to parse Read match/credit outcome and then refresh passes/active. A no-match is a normal successful result.
Expected result 200 confirms source lookup/synchronisation.
Important The frontend never receives Supabase credentials or performs the source lookup.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/passes/sync', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/rider/passes/sync',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/passes/sync');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/passes/sync' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"matched": false,
"creditedMinor": 0,
"balanceMinor": 0
},
"error": null
}
POST /api/v1/rider/quotes
Bearer token
Create a server-calculated fare quote
Endpoint guide What to send, parse and expect Why it exists Calculates a time-limited, server-owned fare before the rider books.
Before you call Obtain real coordinates from your maps/geocoding provider. The frontend must never compute or alter fare values.
Request fields pickup/dropoff each need latitude, longitude and label; distanceKm and durationMinutes are current routing inputs; usePass requests eligible Cruz Pass use.
What to parse Read quote id, expiry, fare breakdown and any pass discount. Show the exact server price and expiry.
Expected result 201 creates an unexpired quote.
Important Discard expired quotes and obtain a new quote; do not reuse one for a different route.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/quotes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"pickup":{"latitude":4.8156,"longitude":7.0498,"label":"Pickup"},"dropoff":{"latitude":4.83,"longitude":7.02,"label":"Destination"},"distanceKm":4.2,"durationMinutes":18,"usePass":true})
});
const result = await response.json(); Python payload = json.loads(r'''{"pickup":{"latitude":4.8156,"longitude":7.0498,"label":"Pickup"},"dropoff":{"latitude":4.83,"longitude":7.02,"label":"Destination"},"distanceKm":4.2,"durationMinutes":18,"usePass":true}''')
response = requests.post(BASE_URL + '/api/v1/rider/quotes',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"pickup":{"latitude":4.8156,"longitude":7.0498,"label":"Pickup"},"dropoff":{"latitude":4.83,"longitude":7.02,"label":"Destination"},"distanceKm":4.2,"durationMinutes":18,"usePass":true}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/quotes');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/quotes' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"pickup":{"latitude":4.8156,"longitude":7.0498,"label":"Pickup"},"dropoff":{"latitude":4.83,"longitude":7.02,"label":"Destination"},"distanceKm":4.2,"durationMinutes":18,"usePass":true}'
{
"success": true,
"data": {
"id": "QOT_45b522d8-141c-4459-81d7-029dfce3912f",
"estimatedFareMinor": 201000,
"passDiscountMinor": 0,
"totalFareMinor": 201000,
"currency": "NGN",
"expiresAt": "2026-08-28T19:51:07.348Z"
},
"error": null
}
POST /api/v1/rider/trips
Bearer token
Book a ride from an unexpired quote
Endpoint guide What to send, parse and expect Why it exists Books a rider-owned trip from one unexpired quote.
Before you call Call quotes first and ensure the rider confirms the returned price. Disable the confirm button while this request is in flight.
Request fields quoteId is usually returned as QOT_<uuid>. driverAcceptanceTimeoutSeconds controls how long each selected driver has to accept before the backend advances; allowed range is 10-300 seconds and the default is 60. The Idempotency-Key prevents accidental duplicate bookings.
What to parse Read trip id and status; use them to render matching/driver-assignment UI.
Expected result 201 creates the trip or returns a safe business error if it cannot be booked.
Important Never construct a trip directly from pickup/dropoff or client-provided money amounts.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"quoteId":"QOT_<uuid>","driverAcceptanceTimeoutSeconds":60})
});
const result = await response.json(); Python payload = json.loads(r'''{"quoteId":"QOT_<uuid>","driverAcceptanceTimeoutSeconds":60}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"quoteId":"QOT_<uuid>","driverAcceptanceTimeoutSeconds":60}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"quoteId":"QOT_<uuid>","driverAcceptanceTimeoutSeconds":60}'
{
"success": false,
"data": null,
"error": {
"code": "ACTIVE_TRIP_EXISTS",
"message": "You already have an active ride",
"details": {
"activeTrip": {
"id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "SEARCHING",
"pickup": {
"label": "Pickup",
"latitude": 4.8156,
"longitude": 7.0498
},
"dropoff": {
"label": "Destination",
"latitude": 4.83,
"longitude": 7.02
},
"estimatedFareMinor": 201000,
"passDiscountMinor": 0,
"finalFareMinor": null,
"currency": "NGN",
"requestedAt": "2026-08-28T14:22:29.895Z",
"tripVersion": 1,
"driverAcceptanceTimeoutSeconds": 60
}
}
}
}
GET /api/v1/rider/trips/:id/matching
Bearer token
Read current matching-session state
Endpoint guide What to send, parse and expect Why it exists Starts or reads durable driver matching for a rider-owned searching trip.
Before you call Book from a server quote first. The booking response begins matching automatically. Use POST /matching/retry only after a no-driver outcome or when the rider explicitly wants a new matching round.
Request fields Replace :id with the searching TRIP_<uuid>. Raw UUIDs are also accepted. No business body is required. POST /matching/retry creates a fresh round and may re-offer the trip to drivers from an earlier round; GET only reads the current matching outcome.
What to parse matchingState is OFFERED or NO_DRIVER_FOUND. When OFFERED, render the waiting state and the returned selectedDriver preview immediately, then listen for trip:state_updated for final assignment.
Expected result POST /matching/retry returns 202 and starts a fresh round; GET /matching returns 200 and only reads the latest state.
Important The backend decides availability, assignment and reassignment. Riders cannot choose or assign drivers.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/matching', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/trips/:id/matching',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/matching');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/trips/:id/matching' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"matchingState": "OFFERED",
"offer": {
"id": "OFF_b2922938-1111-4222-8333-444455556666",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"attemptNo": 3,
"expiresAt": "2026-08-28T18:11:44.285Z",
"status": "OFFERED"
},
"selectedDriver": {
"id": "DRV_2e3149da-835d-4fdc-8d5e-02a5d6c7d55e",
"firstName": "Browser",
"lastName": "Driver",
"profileImageKey": null,
"rating": 5,
"ratingCount": 0,
"vehicle": {
"brand": "ASTRA Ride",
"model": "EV Browser",
"color": "Blue",
"plateNumber": "BRW049960",
"year": 2025
}
},
"acceptancePending": true
},
"error": null
}
POST /api/v1/rider/trips/:id/matching/cancel
Bearer token
Stop the active matching session but retain the trip
Endpoint guide What to send, parse and expect Why it exists Cancels only the active driver-matching session; the trip remains available for retry.
Before you call Use when the rider wants to stop searching without cancelling the ride.
Request fields Replace :id with the active TRIP_<uuid>. No body is required.
What to parse The response returns the cancelled MCH_<uuid> session. Render Search paused and enable Retry matching.
Expected result 200 cancels the session and retracts the active driver offer. 409 means no session is active.
Important Cancelling matching never assigns, cancels or changes another rider trip.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/matching/cancel', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/matching/cancel',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/matching/cancel');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/matching/cancel' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"session": {
"id": "MCH_12345678-1111-4222-8333-444455556666",
"sequence": 2,
"status": "CANCELLED",
"endReason": "RIDER_CANCELLED"
},
"reason": "RIDER_CANCELLED"
},
"error": null
}
POST /api/v1/rider/trips/:id/matching/retry
Bearer token
Start a fresh driver-matching session after it ends
Endpoint guide What to send, parse and expect Why it exists Starts a fresh rider-requested matching round for an existing unassigned trip.
Before you call Use after trip:no_driver_found, after showing the no-driver screen, or when the rider explicitly taps Retry. Do not create another quote or trip.
Request fields Replace :id with the active searching TRIP_<uuid>. Send an empty JSON body. The same trip ID and its driverAcceptanceTimeoutSeconds value are retained; previous OFF_<uuid> records stay as history.
What to parse OFFERED means the selected manual driver has until offer.expiresAt to respond. This window comes from driverAcceptanceTimeoutSeconds saved when the trip was booked (60 seconds by default). Render offer, selectedDriver and acceptancePending:true. DRIVER_ASSIGNED with acceptancePending:false means the selected driver uses AUTO and the trip was assigned immediately. NO_DRIVER_FOUND means show Retry matching and Cancel trip.
Expected result 202 returns tripId, matchingState, offer, selectedDriver and acceptancePending. If an unexpired offer already exists, REST and trip:matching_updated both return that same OFFERED payload without creating a duplicate attempt. When it expires, the backend automatically advances to the next eligible driver using the same per-trip timeout. A retry begins a fresh round: automatic reassignment then skips only drivers already tried in that new round, so drivers from earlier rounds are eligible again. After a completed no-driver round, retry can re-offer the trip to drivers who declined or timed out. A driver is never offered their own rider trip.
Important The rider cannot select a driver. The backend again ranks all currently eligible online drivers.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/matching/retry', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/matching/retry',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/matching/retry');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/matching/retry' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"matchingState": "OFFERED",
"offer": {
"id": "OFF_b2922938-1111-4222-8333-444455556666",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"attemptNo": 3,
"expiresAt": "2026-08-28T18:11:44.285Z",
"status": "OFFERED"
},
"selectedDriver": {
"id": "DRV_2e3149da-835d-4fdc-8d5e-02a5d6c7d55e",
"firstName": "Browser",
"lastName": "Driver",
"profileImageKey": null,
"rating": 5,
"ratingCount": 0,
"vehicle": {
"brand": "ASTRA Ride",
"model": "EV Browser",
"color": "Blue",
"plateNumber": "BRW049960",
"year": 2025
}
},
"acceptancePending": true
},
"error": null
}
POST /api/v1/rider/trips/:id/cancel
Bearer token
Cancel a rider-owned active ride
Endpoint guide What to send, parse and expect Why it exists Cancels an active trip belonging to the authenticated rider.
Before you call Use the active trip id from dashboard or booking response. Confirm intent in the UI before sending.
Request fields Replace :id in Test URL with TRIP_<uuid>. Raw UUIDs are also accepted. reason must be one allowed cancellation reason.
What to parse Render the returned cancelled state and refresh dashboard/pass data; any pass hold/reversal is handled server-side.
Expected result 200 confirms cancellation; an invalid state is a business error.
Important The server verifies ownership and current state, so a rider cannot cancel another rider's trip.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/cancel', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"reason":"CHANGED_PLANS"})
});
const result = await response.json(); Python payload = json.loads(r'''{"reason":"CHANGED_PLANS"}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/cancel',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"reason":"CHANGED_PLANS"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/cancel');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/cancel' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"reason":"CHANGED_PLANS"}'
{
"success": true,
"data": {
"id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "CANCELLED_BY_RIDER",
"pickup": {
"label": "Pickup",
"latitude": 4.8156,
"longitude": 7.0498
},
"dropoff": {
"label": "Destination",
"latitude": 4.83,
"longitude": 7.02
},
"estimatedFareMinor": 201000,
"passDiscountMinor": 0,
"finalFareMinor": null,
"currency": "NGN",
"requestedAt": "2026-08-28T14:22:29.895Z",
"tripVersion": 2
},
"error": null
}
POST /api/v1/rider/places
Bearer token
Save a Home, Work or favourite place
Endpoint guide What to send, parse and expect Why it exists Stores a Home, Work or favourite address for quicker future trip entry.
Before you call Requires an authenticated rider and a geocoded, rider-confirmed address.
Request fields kind, label, address, latitude and longitude describe the saved place. Use the API result rather than a local-only copy.
What to parse Read the saved-place id and updated place data for the dashboard.
Expected result 201 creates the saved place.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/places', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"kind":"HOME","label":"Home","address":"12 Ricewood Estate","latitude":6.431,"longitude":3.492})
});
const result = await response.json(); Python payload = json.loads(r'''{"kind":"HOME","label":"Home","address":"12 Ricewood Estate","latitude":6.431,"longitude":3.492}''')
response = requests.post(BASE_URL + '/api/v1/rider/places',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"kind":"HOME","label":"Home","address":"12 Ricewood Estate","latitude":6.431,"longitude":3.492}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/places');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/places' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"kind":"HOME","label":"Home","address":"12 Ricewood Estate","latitude":6.431,"longitude":3.492}'
{
"success": true,
"data": {
"id": "place-home-1",
"kind": "HOME",
"label": "Home",
"address": "12 Ricewood Estate",
"latitude": 6.431,
"longitude": 3.492
},
"error": null
}
POST /api/v1/rider/trips/:id/reports
Bearer token
Submit a trip issue report
Endpoint guide What to send, parse and expect Why it exists Creates a support/operations report tied to a rider-owned trip.
Before you call Use a completed or relevant trip id. For immediate danger, use emergency services; this is not an SOS channel.
Request fields Replace :id with TRIP_<uuid>. Raw UUIDs are also accepted. category identifies the issue; details gives the operations team context.
What to parse Read the report id/status, show the confirmation UI, and avoid exposing internal investigation notes.
Expected result 201 acknowledges the report.
Important The backend confirms trip ownership and keeps reports available to authorised operations staff only.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/reports', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"category":"INCORRECT_FARE","details":"Please review."})
});
const result = await response.json(); Python payload = json.loads(r'''{"category":"INCORRECT_FARE","details":"Please review."}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/reports',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"category":"INCORRECT_FARE","details":"Please review."}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/reports');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/reports' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"category":"INCORRECT_FARE","details":"Please review."}'
{
"success": true,
"data": {
"id": "trip-report-1",
"status": "OPEN",
"category": "INCORRECT_FARE",
"createdAt": "2026-08-28T18:00:00.000Z"
},
"error": null
}
GET /api/v1/trips/mine
Bearer token
List the authenticated rider's trips
Endpoint guide What to send, parse and expect Why it exists Lists trips owned by the authenticated rider for a history screen.
Before you call Complete onboarding and provide an access token.
Request fields No JSON body. The server determines ownership from the bearer token.
What to parse Render only the returned records; do not merge another rider's locally cached trip data.
Expected result 200 returns the caller's trips.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/mine', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/trips/mine',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/mine');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/trips/mine' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "SEARCHING",
"pickup": {
"label": "Pickup",
"latitude": 4.8156,
"longitude": 7.0498
},
"dropoff": {
"label": "Destination",
"latitude": 4.83,
"longitude": 7.02
},
"estimatedFareMinor": 201000,
"finalFareMinor": null,
"currency": "NGN",
"requestedAt": "2026-08-28T14:22:29.895Z"
}
],
"error": null
}
GET /api/v1/trips/:id
Bearer token
Read an authorised trip
Endpoint guide What to send, parse and expect Why it exists Reads one trip that the authenticated rider is permitted to view.
Before you call Replace :id with an id from a booking, dashboard or trip-history result.
Request fields No JSON body. Bearer authentication is required.
What to parse Use current status and trip fields to refresh a trip-detail screen.
Expected result 200 returns the trip or a safe access/not-found error.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/trips/:id',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/trips/:id' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "DRIVER_ASSIGNED",
"pickup": {
"label": "Pickup"
},
"dropoff": {
"label": "Destination"
},
"estimatedFareMinor": 201000,
"currency": "NGN"
},
"error": null
}
POST /api/v1/trips/:id/rating
Bearer token
Rate a completed trip
Endpoint guide What to send, parse and expect Why it exists Submits rider feedback after a trip.
Before you call Use a completed trip id and show this only when rating is allowed by the backend state.
Request fields Replace :id with TRIP_<uuid>. Raw UUIDs are also accepted. rating is a whole-number star score; comment is optional feedback.
What to parse Show the submitted confirmation and prevent a second submission according to the response.
Expected result 200 confirms the feedback or returns a state/ownership error.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/rating', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"score":5,"comment":"Great ride"})
});
const result = await response.json(); Python payload = json.loads(r'''{"score":5,"comment":"Great ride"}''')
response = requests.post(BASE_URL + '/api/v1/trips/:id/rating',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"score":5,"comment":"Great ride"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/rating');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/trips/:id/rating' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"score":5,"comment":"Great ride"}'
{
"success": true,
"data": {
"id": "trip-rating-1",
"score": 5,
"comment": "Great ride",
"createdAt": "2026-08-28T18:20:00.000Z"
},
"error": null
}
POST /api/v1/payments/intents
Bearer token
Create payment instructions for a completed trip
Endpoint guide What to send, parse and expect Why it exists Creates the payment instruction for a completed rider trip.
Before you call The trip must be completed and unpaid. Use the trip id from the final trip state.
Request fields tripId is usually TRIP_<uuid>. Raw UUIDs are also accepted. method is BANK_TRANSFER or CASH.
What to parse For BANK_TRANSFER, render paymentInstructions exactly as returned; for CASH, show the amount due and wait for driver confirmation.
Expected result 201 returns the payment intent.
Important Never accept account details, amount or payment status from a client-side calculation.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/payments/intents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"tripId":"TRIP_<uuid>","method":"BANK_TRANSFER"})
});
const result = await response.json(); Python payload = json.loads(r'''{"tripId":"TRIP_<uuid>","method":"BANK_TRANSFER"}''')
response = requests.post(BASE_URL + '/api/v1/payments/intents',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"tripId":"TRIP_<uuid>","method":"BANK_TRANSFER"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/payments/intents');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/payments/intents' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"tripId":"TRIP_<uuid>","method":"BANK_TRANSFER"}'
{
"success": true,
"data": {
"id": "PAY_9b9c96b2-5b0b-4c1b-8d5d-1c02f0a6f901",
"tripId": "TRIP_6658fa14-4931-42b3-abdf-08bb9e46090b",
"riderId": "RDR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"fleetId": "FLT_b1fe6573-3a25-4d13-b41e-55143176d849",
"amountMinor": 201000,
"currency": "NGN",
"method": "BANK_TRANSFER",
"provider": "NONE",
"status": "PENDING",
"internalReference": "CRUIZ_abc123",
"providerReference": null,
"paymentInstructions": {
"method": "BANK_TRANSFER",
"bankName": "Moniepoint",
"accountNumber": "1234567890",
"accountName": "ASTRA Ride Mobility",
"amountMinor": 201000,
"currency": "NGN"
},
"expiresAt": "2026-08-28T19:00:00.000Z",
"paidAt": null,
"failedAt": null,
"createdAt": "2026-08-28T18:30:00.000Z"
},
"error": null
}
GET /api/v1/payments/intents/:id
Bearer token
Read a payment intent
Endpoint guide What to send, parse and expect Why it exists Retrieves the current payment-intent state for the rider's payment screen.
Before you call Replace :id with PAY_<uuid> from intent creation. Raw UUIDs are also accepted.
Request fields No JSON body. Bearer authentication enforces rider ownership.
What to parse Use status, amountMinor and paymentInstructions to render the payment state.
Expected result 200 returns the intent or an access error.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/payments/intents/:id', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/payments/intents/:id',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/payments/intents/:id');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/payments/intents/:id' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "PAY_9b9c96b2-5b0b-4c1b-8d5d-1c02f0a6f901",
"tripId": "TRIP_6658fa14-4931-42b3-abdf-08bb9e46090b",
"riderId": "RDR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"fleetId": "FLT_b1fe6573-3a25-4d13-b41e-55143176d849",
"amountMinor": 201000,
"currency": "NGN",
"method": "BANK_TRANSFER",
"provider": "NONE",
"status": "PENDING",
"internalReference": "CRUIZ_abc123",
"providerReference": null,
"paymentInstructions": {
"method": "BANK_TRANSFER",
"bankName": "Moniepoint",
"accountNumber": "1234567890",
"accountName": "ASTRA Ride Mobility",
"amountMinor": 201000,
"currency": "NGN"
},
"expiresAt": "2026-08-28T19:00:00.000Z",
"paidAt": null,
"failedAt": null,
"createdAt": "2026-08-28T18:30:00.000Z"
},
"error": null
}
POST /api/v1/payments/intents/:id/verify
Bearer token
Verify a payment intent
Endpoint guide What to send, parse and expect Why it exists Asks the server to verify a provider-backed payment intent safely.
Before you call Use only after the rider has made the instructed payment; the server, not the frontend, decides whether it is paid.
Request fields Replace :id with PAY_<uuid>. Raw UUIDs are also accepted. No JSON business fields are required.
What to parse Render returned status. Treat pending/failed states as server truth and offer a refresh/retry UI.
Expected result 200 returns the verified payment state.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/payments/intents/:id/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/payments/intents/:id/verify',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/payments/intents/:id/verify');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/payments/intents/:id/verify' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "9b9c96b2-5b0b-4c1b-8d5d-1c02f0a6f901",
"status": "SUCCESSFUL",
"paidAt": "2026-08-28T18:30:00.000Z"
},
"error": null
}
GET /api/v1/rider/safety/trusted-contacts
Bearer token
List trusted safety contacts
Endpoint guide What to send, parse and expect Why it exists Manages people the rider trusts for safety-related trip sharing.
Before you call Requires an access token. Display contacts only to their owner.
Request fields POST uses name, E.164 phone and idempotencyKey. GET has no body. DELETE replaces :id with a returned contact id.
What to parse Use id/name/phone from the response; never expose this list in shared-trip data.
Expected result POST 201 creates or updates; GET 200 lists; DELETE 200 confirms removal.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/safety/trusted-contacts', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/safety/trusted-contacts',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/safety/trusted-contacts');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/safety/trusted-contacts' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "trusted-1",
"name": "Ada Sister",
"phone": "+2349054568692",
"createdAt": "2026-08-28T12:00:00.000Z"
}
],
"error": null
}
POST /api/v1/rider/safety/trusted-contacts
Bearer token
Add or update a trusted safety contact
Endpoint guide What to send, parse and expect Why it exists Manages people the rider trusts for safety-related trip sharing.
Before you call Requires an access token. Display contacts only to their owner.
Request fields POST uses name, E.164 phone and idempotencyKey. GET has no body. DELETE replaces :id with a returned contact id.
What to parse Use id/name/phone from the response; never expose this list in shared-trip data.
Expected result POST 201 creates or updates; GET 200 lists; DELETE 200 confirms removal.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/safety/trusted-contacts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"name":"Ada's sister","phone":"+2349054568692"})
});
const result = await response.json(); Python payload = json.loads(r'''{"name":"Ada's sister","phone":"+2349054568692"}''')
response = requests.post(BASE_URL + '/api/v1/rider/safety/trusted-contacts',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"name":"Ada's sister","phone":"+2349054568692"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/safety/trusted-contacts');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/safety/trusted-contacts' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"name":"Ada's sister","phone":"+2349054568692"}'
{
"success": true,
"data": {
"id": "trusted-1",
"name": "Ada Sister",
"phone": "+2349054568692",
"createdAt": "2026-08-28T12:00:00.000Z"
},
"error": null
}
DELETE /api/v1/rider/safety/trusted-contacts/:id
Bearer token
Remove a trusted safety contact
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/safety/trusted-contacts/:id', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.delete(BASE_URL + '/api/v1/rider/safety/trusted-contacts/:id',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/safety/trusted-contacts/:id');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X DELETE '<base-url>/api/v1/rider/safety/trusted-contacts/:id' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "trusted-1",
"deleted": true
},
"error": null
}
POST /api/v1/rider/trips/:id/share-links
Bearer token
Create a revocable live-trip share link
Endpoint guide What to send, parse and expect Why it exists Creates a revocable, time-limited public trip-share link.
Before you call The rider must own the trip. Make it clear to the rider that anyone holding the link can see limited live-trip data.
Request fields POST accepts expiresInMinutes and idempotencyKey. Replace :id with TRIP_<uuid>. Raw UUIDs are also accepted. DELETE needs shareId from creation.
What to parse Use sharePath/token only to form the share URL. Treat it as secret and never log it.
Expected result POST 201 creates a link; DELETE 200 revokes it.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/share-links', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"expiresInMinutes":120})
});
const result = await response.json(); Python payload = json.loads(r'''{"expiresInMinutes":120}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/share-links',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"expiresInMinutes":120}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/share-links');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/share-links' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"expiresInMinutes":120}'
{
"success": true,
"data": {
"id": "share-1",
"token": "<share-token>",
"expiresAt": "2026-08-28T20:00:00.000Z",
"sharePath": "/api/v1/rider/shared-trips/<share-token>"
},
"error": null
}
DELETE /api/v1/rider/trips/:id/share-links/:shareId
Bearer token
Revoke a live-trip share link
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/share-links/:shareId', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.delete(BASE_URL + '/api/v1/rider/trips/:id/share-links/:shareId',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/share-links/:shareId');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X DELETE '<base-url>/api/v1/rider/trips/:id/share-links/:shareId' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "share-1",
"revoked": true
},
"error": null
}
GET /api/v1/rider/shared-trips/:token
Public
Open a public, limited trip-share view
Endpoint guide What to send, parse and expect Why it exists Returns the deliberately limited live trip view for a holder of an unexpired share link.
Before you call This is public and must only be opened with a token received through a trusted sharing channel.
Request fields Replace :token with the opaque token from share-link creation. No bearer token or JSON body.
What to parse Render the returned status, route labels, limited driver/vehicle details and fresh location. Do not request rider contact data.
Expected result 200 returns an unexpired share; 404 hides whether a link ever existed.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/shared-trips/:token', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/shared-trips/:token',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/shared-trips/:token');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/shared-trips/:token' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"trip": {
"id": "trip-share-1",
"status": "DRIVER_EN_ROUTE",
"pickup": {
"label": "Pickup"
},
"dropoff": {
"label": "Destination"
},
"driver": {
"firstName": "Browser",
"profileImageKey": null
},
"vehicle": {
"brand": "ASTRA Ride",
"model": "EV Browser",
"color": "Blue"
},
"driverLocation": {
"latitude": 4.8156,
"longitude": 7.0498,
"recordedAt": "2026-08-28T18:04:00.000Z"
}
},
"expiresAt": "2026-08-28T20:00:00.000Z"
},
"error": null
}
POST /api/v1/rider/trips/:id/safety-incidents
Bearer token
Raise SOS or another trip safety incident
Endpoint guide What to send, parse and expect Why it exists Raises a safety incident, including SOS and extra-money requests, to authorised operations staff.
Before you call The rider must own the trip. In a life-threatening emergency, the app must also direct the rider to local emergency services.
Request fields type, description, optional coordinates and idempotencyKey. Replace :id with TRIP_<uuid>. Raw UUIDs are also accepted.
What to parse Read incident id, status and severity, then show a clear submitted/ongoing-safety state.
Expected result 201 opens the incident with HIGH or CRITICAL severity.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/safety-incidents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"type":"SOS","description":"I need help","latitude":6.431,"longitude":3.492})
});
const result = await response.json(); Python payload = json.loads(r'''{"type":"SOS","description":"I need help","latitude":6.431,"longitude":3.492}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/safety-incidents',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"type":"SOS","description":"I need help","latitude":6.431,"longitude":3.492}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/safety-incidents');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/safety-incidents' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"type":"SOS","description":"I need help","latitude":6.431,"longitude":3.492}'
{
"success": true,
"data": {
"id": "4a57456d-32d1-439d-9d01-070e959babf9",
"status": "OPEN",
"severity": "CRITICAL",
"createdAt": "2026-08-28T18:10:00.000Z"
},
"error": null
}
GET /api/v1/rider/trips/:id/driver-details
Bearer token
Read assigned driver, vehicle and latest location
Endpoint guide What to send, parse and expect Why it exists Safely exposes the assigned driver, vehicle and latest trip-scoped location to the trip's rider.
Before you call Call only after assignment. The rider must own the trip.
Request fields Replace :id with TRIP_<uuid>. Raw UUIDs are also accepted. No JSON body.
What to parse Render name, approved profile image, aggregate rating, vehicle details and last location timestamp.
Expected result 200 returns details; 409 means no driver is assigned yet.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/driver-details', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/trips/:id/driver-details',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/driver-details');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/trips/:id/driver-details' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"driver": {
"id": "DRV_2e3149da-835d-4fdc-8d5e-02a5d6c7d55e",
"firstName": "Browser",
"lastName": "Driver",
"profileImageKey": null,
"rating": 5,
"ratingCount": 2
},
"vehicle": {
"brand": "ASTRA Ride",
"model": "EV Browser",
"color": "Blue",
"plateNumber": "BRW049960",
"year": 2025
},
"location": {
"latitude": 4.8156,
"longitude": 7.0498,
"recordedAt": "2026-08-28T18:04:00.000Z"
}
},
"error": null
}
GET /api/v1/rider/trips/:id/driver-locationNEW
Bearer token
Recover the assigned driver’s latest fresh map position
Endpoint guide What to send, parse and expect Why it exists Fetches the assigned car position on map open or socket reconnect.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields Replace :id with the rider-owned trip ID. No body.
What to parse Read data.location.latitude, longitude, heading and recordedAt. Null means no GPS in the last two minutes.
Expected result 200 returns tripId and location; 409 if no driver is assigned.
Important Only the trip rider may read this position.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/driver-location', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/trips/:id/driver-location',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/driver-location');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/trips/:id/driver-location' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"location": {
"latitude": 4.816,
"longitude": 7.05,
"heading": 90,
"speedKph": 12,
"recordedAt": "2026-08-28T18:04:00.000Z",
"receivedAt": "2026-08-28T18:04:01.000Z"
}
},
"error": null
}
POST /api/v1/rider/trips/:id/locationNEW
Bearer token
Share the rider’s live position with the assigned driver
Endpoint guide What to send, parse and expect Why it exists Updates the rider position before pickup and notifies the assigned driver.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields Valid GPS latitude/longitude, optional accuracyM (0-1000), and explicit shareWithDriver:true.
What to parse The response contains the stored position and timestamp. Send new samples while the rider consents and the trip is awaiting pickup.
Expected result 201 returns tripId and location; 409 when the trip is unassigned or no longer awaiting pickup.
Important Only the trip rider may publish; no location is shared after boarding.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/location', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"latitude":4.8156,"longitude":7.0498,"accuracyM":12,"shareWithDriver":true})
});
const result = await response.json(); Python payload = json.loads(r'''{"latitude":4.8156,"longitude":7.0498,"accuracyM":12,"shareWithDriver":true}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/location',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"latitude":4.8156,"longitude":7.0498,"accuracyM":12,"shareWithDriver":true}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/location');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/location' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"latitude":4.8156,"longitude":7.0498,"accuracyM":12,"shareWithDriver":true}'
{
"success": true,
"data": {
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"location": {
"latitude": 4.8156,
"longitude": 7.0498,
"accuracyM": 12,
"recordedAt": "2026-08-28T18:04:00.000Z"
}
},
"error": null
}
DELETE /api/v1/rider/trips/:id/locationNEW
Bearer token
Stop sharing the rider’s moving position immediately
Endpoint guide What to send, parse and expect Why it exists Clears the latest shared rider GPS point and tells the driver to remove the moving-rider marker.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields Replace :id with the rider-owned trip ID. No body.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Expected result 200 returns stopped:true even when no point exists.
Important Only the trip rider may stop their sharing.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/location', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.delete(BASE_URL + '/api/v1/rider/trips/:id/location',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/location');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X DELETE '<base-url>/api/v1/rider/trips/:id/location' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"stopped": true
},
"error": null
}
GET /api/v1/rider/places
Bearer token
List saved Home, Work and favourite places
Endpoint guide What to send, parse and expect Why it exists Stores a Home, Work or favourite address for quicker future trip entry.
Before you call Requires an authenticated rider and a geocoded, rider-confirmed address.
Request fields kind, label, address, latitude and longitude describe the saved place. Use the API result rather than a local-only copy.
What to parse Read the saved-place id and updated place data for the dashboard.
Expected result 201 creates the saved place.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/places', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/places',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/places');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/places' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "place-home-1",
"kind": "HOME",
"label": "Home",
"address": "12 Ricewood Estate",
"latitude": 6.431,
"longitude": 3.492
}
],
"error": null
}
DELETE /api/v1/rider/places/:id
Bearer token
Remove a saved place
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/places/:id', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.delete(BASE_URL + '/api/v1/rider/places/:id',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/places/:id');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X DELETE '<base-url>/api/v1/rider/places/:id' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "place-home-1",
"deleted": true
},
"error": null
}
GET /api/v1/rider/support/tickets
Bearer token
List rider-owned support tickets
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/support/tickets', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/support/tickets',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/support/tickets');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/support/tickets' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "support-1",
"tripId": null,
"category": "LOST_ITEM",
"subject": "Phone left in vehicle",
"status": "OPEN",
"priority": "HIGH",
"createdAt": "2026-08-28T18:00:00.000Z",
"updatedAt": "2026-08-28T18:00:00.000Z"
}
],
"error": null
}
POST /api/v1/rider/support/tickets
Bearer token
Open a support or lost-item ticket
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/support/tickets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"category":"LOST_ITEM","subject":"Phone left in vehicle","details":"Black phone on the rear seat","priority":"HIGH"})
});
const result = await response.json(); Python payload = json.loads(r'''{"category":"LOST_ITEM","subject":"Phone left in vehicle","details":"Black phone on the rear seat","priority":"HIGH"}''')
response = requests.post(BASE_URL + '/api/v1/rider/support/tickets',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"category":"LOST_ITEM","subject":"Phone left in vehicle","details":"Black phone on the rear seat","priority":"HIGH"}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/support/tickets');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/support/tickets' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"category":"LOST_ITEM","subject":"Phone left in vehicle","details":"Black phone on the rear seat","priority":"HIGH"}'
{
"success": true,
"data": {
"id": "support-1",
"status": "OPEN",
"priority": "HIGH",
"createdAt": "2026-08-28T18:00:00.000Z"
},
"error": null
}
GET /api/v1/rider/support/tickets/:id
Bearer token
Read an owned support ticket conversation
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/support/tickets/:id', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/support/tickets/:id',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/support/tickets/:id');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/support/tickets/:id' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"id": "support-1",
"subject": "Phone left in vehicle",
"status": "IN_PROGRESS",
"messages": [
{
"id": "reply-1",
"body": "The driver is checking the vehicle.",
"author": "SUPPORT",
"createdAt": "2026-08-28T18:10:00.000Z"
}
]
},
"error": null
}
POST /api/v1/rider/support/tickets/:id/messages
Bearer token
Reply to an owned support ticket
Endpoint guide What to send, parse and expect Why it exists Use this endpoint to perform the action described above.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields The JSON editor contains a runnable example. Required field rules are returned as error.details.fieldErrors when validation fails.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/support/tickets/:id/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"body":"Here is the requested information."})
});
const result = await response.json(); Python payload = json.loads(r'''{"body":"Here is the requested information."}''')
response = requests.post(BASE_URL + '/api/v1/rider/support/tickets/:id/messages',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"body":"Here is the requested information."}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/support/tickets/:id/messages');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/support/tickets/:id/messages' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"body":"Here is the requested information."}'
{
"success": true,
"data": {
"replayed": false,
"status": "IN_PROGRESS",
"message": {
"id": "reply-2",
"body": "It has a black case.",
"author": "CUSTOMER",
"createdAt": "2026-08-28T18:12:00.000Z"
}
},
"error": null
}
GET /api/v1/rider/trips/:id/messages
Bearer token
Read authorised rider-driver trip chat
Endpoint guide What to send, parse and expect Why it exists Provides an authorised, trip-scoped conversation between the rider and assigned driver.
Before you call Call only while an assigned trip is active.
Request fields GET has no body. POST requires body (1-2,000 characters) and an idempotency key. Replace :id with the active trip UUID.
What to parse Render messages in created-time order. Subscribe to message:received for live delivery and refetch this endpoint after reconnecting.
Expected result GET 200 lists the rider messages; POST 201 creates one. 409 means chat is unavailable.
Important The backend checks rider ownership, driver assignment and active trip status; the rider cannot supply a recipient id.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/messages', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/trips/:id/messages',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/messages');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/trips/:id/messages' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": [
{
"id": "msg-1",
"trip_id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"sender_id": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipient_id": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"body": "I am outside",
"type": "TRIP",
"created_at": "2026-08-28T18:02:00.000Z"
}
],
"error": null
}
POST /api/v1/rider/trips/:id/messages
Bearer token
Send an authorised message to the assigned driver
Endpoint guide What to send, parse and expect Why it exists Provides an authorised, trip-scoped conversation between the rider and assigned driver.
Before you call Call only while an assigned trip is active.
Request fields GET has no body. POST requires body (1-2,000 characters) and an idempotency key. Replace :id with the active trip UUID.
What to parse Render messages in created-time order. Subscribe to message:received for live delivery and refetch this endpoint after reconnecting.
Expected result GET 200 lists the rider messages; POST 201 creates one. 409 means chat is unavailable.
Important The backend checks rider ownership, driver assignment and active trip status; the rider cannot supply a recipient id.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({"body":"I will be outside in two minutes."})
});
const result = await response.json(); Python payload = json.loads(r'''{"body":"I will be outside in two minutes."}''')
response = requests.post(BASE_URL + '/api/v1/rider/trips/:id/messages',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{"body":"I will be outside in two minutes."}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/messages');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/rider/trips/:id/messages' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{"body":"I will be outside in two minutes."}'
{
"success": true,
"data": {
"id": "msg-2",
"trip_id": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"sender_id": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipient_id": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"body": "I will be outside in two minutes.",
"type": "TRIP",
"created_at": "2026-08-28T18:03:00.000Z"
},
"error": null
}
GET /api/v1/rider/trips/:id/pickup-verification
Bearer token
Read the private pickup PIN or QR payload
Endpoint guide What to send, parse and expect Why it exists Shows the rider-only four-digit pickup PIN, or a signed QR payload when that fleet has enabled QR pickup confirmation.
Before you call Use only after a driver is assigned. This capability is fleet-admin controlled; required:false means the normal trip-state flow remains active.
Request fields Replace :id with the current rider-owned TRIP_<uuid>. Raw UUIDs are also accepted. There is no request body.
What to parse When required is true, render pin only on the private rider screen. For QR mode, render qrPayload as a QR code; never log, share or persist it.
Expected result 200 returns required, mode, pin and optional qrPayload. 409 means there is no assigned driver or the trip is not in a pickup state.
Important The PIN is derived server-side and its bcrypt verifier is stored instead of plaintext. Drivers cannot retrieve this endpoint.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/rider/trips/:id/pickup-verification', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/rider/trips/:id/pickup-verification',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/rider/trips/:id/pickup-verification');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/rider/trips/:id/pickup-verification' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"required": true,
"mode": "PIN",
"pin": "7878",
"qrPayload": null
},
"error": null
}
POST /api/v1/trips/:id/calls
Bearer token
Start an in-trip voice call
Endpoint guide What to send, parse and expect Why it exists Ring the assigned rider or driver for this active trip.
Before you call Use the assigned TRIP_<uuid> and a bearer session. Send Idempotency-Key for safe retries.
Request fields Optional `{ ringTimeoutSeconds }` integer: 10-300 seconds; omitted uses 120 seconds. The backend chooses the recipient and LiveKit room.
What to parse Render RINGING and expiresAt. Wait for call:state_changed before requesting a join token.
Expected result 201 returns callId, tripId, status, version, participant IDs and timestamps. 409 means the trip is not eligible; 503 means LiveKit is disabled.
Important Only the current assigned driver and rider can initiate. No LiveKit token is issued before answer.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/calls', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({ "ringTimeoutSeconds": 120 })
});
const result = await response.json(); Python payload = json.loads(r'''{ "ringTimeoutSeconds": 120 }''')
response = requests.post(BASE_URL + '/api/v1/trips/:id/calls',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{ "ringTimeoutSeconds": 120 }
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/calls');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/trips/:id/calls' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{ "ringTimeoutSeconds": 120 }'
{
"success": true,
"data": {
"callId": "CAL_bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "RINGING",
"version": 1,
"callerUserId": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipientUserId": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"expiresAt": "2026-10-02T18:00:30.000Z",
"acceptedAt": null,
"activeAt": null,
"endedAt": null,
"createdAt": "2026-10-02T18:00:00.000Z"
},
"error": null
}
GET /api/v1/trips/:id/calls/active
Bearer token
Recover active call state
Endpoint guide What to send, parse and expect Why it exists Read the current call after app resume or socket reconnect.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Expected result 200 returns a call summary or null.
Important Restricted to the assigned trip pair.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/calls/active', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.get(BASE_URL + '/api/v1/trips/:id/calls/active',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/calls/active');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X GET '<base-url>/api/v1/trips/:id/calls/active' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"callId": "CAL_bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "RINGING",
"version": 1,
"callerUserId": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipientUserId": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"expiresAt": "2026-10-02T18:00:30.000Z",
"acceptedAt": null,
"activeAt": null,
"endedAt": null,
"createdAt": "2026-10-02T18:00:00.000Z"
},
"error": null
}
POST /api/v1/trips/:id/calls/:callId/accept
Bearer token
Accept an incoming call
Endpoint guide What to send, parse and expect Why it exists The recipient accepts before the call response deadline in expiresAt.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields Use CAL_<uuid> from call:incoming; no body.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Expected result 200 returns CONNECTING; 409 means timed out or already decided.
Important Caller cannot accept their own call.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/calls/:callId/accept', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/trips/:id/calls/:callId/accept',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/calls/:callId/accept');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/trips/:id/calls/:callId/accept' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"callId": "CAL_bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "CONNECTING",
"version": 2,
"callerUserId": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipientUserId": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"expiresAt": "2026-10-02T18:00:30.000Z",
"acceptedAt": "2026-10-02T18:00:12.000Z",
"activeAt": null,
"endedAt": null,
"createdAt": "2026-10-02T18:00:00.000Z"
},
"error": null
}
POST /api/v1/trips/:id/calls/:callId/decline
Bearer token
Decline an incoming call
Endpoint guide What to send, parse and expect Why it exists The recipient declines the ringing call.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Expected result 200 returns DECLINED and emits call:ended.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/calls/:callId/decline', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/trips/:id/calls/:callId/decline',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/calls/:callId/decline');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/trips/:id/calls/:callId/decline' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"callId": "CAL_bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "DECLINED",
"version": 2,
"callerUserId": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipientUserId": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"expiresAt": "2026-10-02T18:00:30.000Z",
"acceptedAt": null,
"activeAt": null,
"endedAt": "2026-10-02T18:00:12.000Z",
"createdAt": "2026-10-02T18:00:00.000Z"
},
"error": null
}
POST /api/v1/trips/:id/calls/:callId/join-token
Bearer token
Get own LiveKit microphone token
Endpoint guide What to send, parse and expect Why it exists After acceptance, get a short-lived credential for this device only.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
Request fields No body. Call separately on the rider and driver app.
What to parse Pass serverUrl and token to the LiveKit SDK room.connect; then enable microphone. Never persist the token.
Expected result 200 returns callId, serverUrl, token, identity and expiresAt; Cache-Control: no-store.
Important Server signs one-room, microphone-only, non-admin grants after rechecking assignment and call status.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/calls/:callId/join-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/trips/:id/calls/:callId/join-token',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/calls/:callId/join-token');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/trips/:id/calls/:callId/join-token' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"callId": "CAL_bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"serverUrl": "wss://your-project.livekit.cloud",
"token": "<short-lived-livekit-token>",
"identity": "rider:bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"expiresAt": "2026-10-02T18:02:00.000Z"
},
"error": null
}
POST /api/v1/trips/:id/calls/:callId/end
Bearer token
End an in-trip voice call
Endpoint guide What to send, parse and expect Why it exists Either participant terminates the call.
Before you call Use the method and authentication badge shown above. Replace every placeholder with a value returned by an earlier API call.
What to parse Check success first. On success, read data; on failure, render error.code and error.message.
Expected result 200 returns ENDED and emits call:ended; repeated end is idempotent.
Request structure and code samples Node.js · Python · PHP · cURL Replace angle-bracket values with returned values. Use the raw access token only in the Authorization header.
Node.js const response = await fetch(BASE_URL + '/api/v1/trips/:id/calls/:callId/end', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer <access-token>',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({})
});
const result = await response.json(); Python payload = json.loads(r'''{}''')
response = requests.post(BASE_URL + '/api/v1/trips/:id/calls/:callId/end',
headers={'Idempotency-Key': str(uuid.uuid4())},
json=payload)
result = response.json() PHP $payload = <<<'JSON'
{}
JSON;
$ch = curl_init($baseUrl . '/api/v1/trips/:id/calls/:callId/end');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: <uuid>'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]); cURL curl -X POST '<base-url>/api/v1/trips/:id/calls/:callId/end' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <uuid>' \
--data '{}'
{
"success": true,
"data": {
"callId": "CAL_bbd4b8e1-e3b6-4dbb-bc7d-ea5df8592cbc",
"tripId": "TRIP_a40cd66e-e88f-4729-9f03-49d66aad7a12",
"status": "ENDED",
"version": 2,
"callerUserId": "USR_32f265f3-efee-4bbc-86d5-ed50c21c0267",
"recipientUserId": "USR_0cc3dcfc-ce9c-4e79-bf09-3c77a0b6ac34",
"expiresAt": "2026-10-02T18:00:30.000Z",
"acceptedAt": null,
"activeAt": null,
"endedAt": "2026-10-02T18:00:12.000Z",
"createdAt": "2026-10-02T18:00:00.000Z"
},
"error": null
}
Errors and safety Responses use JSON with a stable error code. Mutating requests receive an automatic idempotency key in the test console. Never paste production credentials here.