18 KiB
Miler (Rider App) — Backend API Specification
Audience: Doormile backend team Purpose: This is the contract the Miler rider app needs the backend to implement so we can connect everything and go live. It documents (1) what the app already sends and expects today, and (2) the changes required to ship the mixed pickup + delivery route feature.
How to use this doc: Design/confirm each endpoint below, then send back the finalized doc (exact URLs, request/response JSON, and any field renames). We then wire the app to your final contract and go live.
Status legend for each endpoint: [LIVE] already called by the current app — keep the contract stable. [CHANGE] needs a change/addition before go-live. [NEW] not built yet.
1. Global conventions
1.1 Base URLs / environments
The app switches between dev and live by an environment flag. Please expose the same path on both:
DEV base: https://jupiter.doormile.app/dev/api
LIVE base: https://jupiter.doormile.app/live/api
⚠️ Must fix before go-live: Today a few write endpoints (update pickup, create rider log, break logs) point at a second host
https://queue.workolik.com/live/api/..., and the app currently has to bypass TLS cert validation and hard-code the server IP (66.116.225.226) because carrier DNS returns broken CDN nodes for that host and the certificate doesn't validate. Please serve everything from one host (jupiter.doormile.app) with a valid TLS certificate and correct DNS so we can remove the SSL-bypass hack. This is a security and reliability blocker.
1.2 Auth
- The app does not currently send a bearer token — requests are keyed by
userid. Please tell us the intended auth model. Recommended: return a JWT/session token from login and requireAuthorization: Bearer <token>on every other call. If you keepuserid-only, confirm that explicitly.
1.3 Request/response format
- Content type:
application/json(both directions). - Standard response envelope (already used by read endpoints — please use it everywhere):
{
"code": 200,
"status": true,
"message": "Success",
"details": [ ... ] // object OR array — the actual payload
}
- The app reads the payload from
detailsfirst, then falls back todata, then the root. Please standardize ondetails. - On error return
status: false, a non-2xx HTTP code, and a human-readablemessage.
1.4 Formats
- Dates (query params):
YYYY-MM-DD(e.g.2026-07-18). - Timestamps (bodies): full date-time string, currently
YYYY-MM-DD HH:mm:ss. Confirm timezone — please use IST consistently and state it. - Lat/Long: strings, decimal degrees (e.g.
"12.9716"). Do not truncate precision. - Money: number (₹). Confirm 2-decimal.
- Booleans / flags: confirm whether you use
true/falseor1/0— the app currently tolerates both forstatusbut please pick one.
1.5 Cache-busting
Read endpoints receive a t=<epoch-millis> query param — ignore it server-side; it exists to defeat caching.
2. Authentication
2.1 Rider Login [LIVE]
POST /v2/users/rider/login
Request:
{
"contactno": "9876543210",
"devicetype": "android", // "android" | "ios"
"configid": 123,
"deviceid": "<device-uuid>",
"userfcmtoken": "<fcm-token>",
"pin": 1234 // optional; sent on PIN login
}
Response details (object) — every field below is consumed by the app, so keep them:
{
"userid": 1001,
"riderid": 55,
"partnerid": 12,
"configid": 123,
"shiftid": 7,
"logid": 0,
"logseconds": 0,
"tenantid": 3,
"locationid": 9,
"applocationid": 9,
"roleid": 2,
"authmode": 1,
"authname": "…",
"firstname": "Suriya",
"lastname": "K",
"username": "suriya",
"email": "…",
"onduty": 0, // 0 = off duty, 1 = on duty
"starttime": "09:00", // shift window (display)
"endtime": "18:00",
"pickupradius": 100, // meters — geofence radius for arrived/pickup
"fuelcharge": 5.0, // ₹ per km (rider payout)
"firstmilecharge": 0.0, // per-km first-mile charge (alias: firstmilecharges)
"userfcmtoken": "<echoed>"
}
Notes:
authmodedecides the flow (e.g. whether a PIN step is required). Please document the possible values.- If a rider needs to set a PIN on first login, tell us how that state is signaled.
2.2 Update PIN [LIVE]
PUT /v2/users/update
{ "userid": 1001, "pin": 1234 }
Response: standard envelope, status: true on success.
3. Rider duty log (On/Off Duty, breaks)
The rider goes On Duty → works stops → Off Duty. These calls power the duty timer and location tracking.
3.1 Create Rider Log (go On Duty) [LIVE]
POST /v2/partners/createriderlog
Request:
{
"logid": 0, // 0 → server assigns new logid; returned in response
"userid": 1001,
"partnerid": 12,
"shiftid": 7,
"logdate": "2026-07-18T09:00:00",
"login": "2026-07-18 09:00:00", // on-duty timestamp
"onduty": 1,
"status": "online",
"latitude": "12.9716",
"longitude": "77.5946",
"raw_latitude": "12.9716",
"raw_longitude": "77.5946",
"velocity_lat": "0",
"velocity_lng": "0",
"speed": "0",
"heading": "0",
"contactno": "9876543210",
"tenantid": 3,
"locationid": 9,
"applocationid": 9,
"userfcmtoken": "<fcm-token>",
"orderid": "" // optional; current stop context if any
}
Response must return the new logid (the app stores it and uses it for updates). Return it in details.
3.2 Update Rider Log (heartbeat / go Off Duty) [LIVE]
PUT /v1/partners/updateriderlog
Sent periodically as a location heartbeat and once when going Off Duty:
{
"logid": 4567,
"userid": 1001,
"logdate": "2026-07-18T13:00:00",
"latitude": "12.9722",
"longitude": "77.5950",
"speed": "0",
"heading": "0",
"status": "online", // "offline" when going off duty
"orderid": ""
}
Confirm the exact field(s) used to mark Off Duty (e.g. status: "offline" and/or logout timestamp + onduty: 0). Please state it explicitly.
3.3 Get Rider Log [LIVE]
GET /v1/partners/getriderlog?userid=1001 → current log record (used to restore duty state on app restart). Return logid, onduty, login time, accumulated seconds, etc.
3.4 Get Rider Count [LIVE]
GET /v1/partners/getridercount?userid=1001 → counts for the dashboard (e.g. completed stops today). Please document exact fields.
3.5 Break logs [LIVE]
POST /v2/partners/createbreaklog— start break.PUT /v2/partners/updatebreaklog— end break.
Please document the exact request bodies (rider id, logid, start/end timestamps, break type).
4. Route & Stops (the core flow)
Domain recap for the backend: For a booked time slot, the hub/admin assigns a rider an ordered route of stops. The rider starts at a hub, works stops in fixed sequence (cannot reorder), can skip and resume a stop, and after the last stop returns to the same hub. Each stop is either a PICKUP or a DELIVERY (see §4.5 — this is the key new requirement).
4.1 Get Pickup Queue (assigned/pending stops) [LIVE]
GET /v2/pickups/getpickupqueues?userid=1001&fromdate=2026-07-18&todate=2026-07-18&orderstatus=<optional>&t=<epoch>
Returns details = array of stop objects. Fields the app reads today (please keep these names, lowercase):
| Field | Type | Meaning |
|---|---|---|
orderid |
string/int | Order identifier shown to rider |
pickupid |
int | Stop id — primary key for all status updates |
orderheaderid |
int | Order header id (sent back on updates) |
pickuplocationid |
int | Location id of the stop |
orderstatus |
string | Current status (see §4.6 lifecycle) |
step |
int | Sequence position in the route (1..N) — defines fixed order |
pickupcustomer |
string | Customer / store name |
pickupcontactno |
string | Customer phone (Call button) |
pickupaddress |
string | Stop address |
pickuplat / pickuplong |
string | Stop coordinates (geofence + navigation) |
dropaddress |
string | Drop address (delivery stops) |
droplat / droplon |
string | Drop coordinates (delivery stops) |
collectionamt |
number | Amount to collect at this stop (0 = none) |
pickupamt |
number | Pickup charge |
eta / expected_pickup_time |
string | ETA / expected time (display) |
tenantid / tenantname |
int/string | Tenant |
starttime |
string | Slot / assignment start |
⚠️ Casing: the app has seen both
pickuplat/PickupLatandorderid/OrderIdvariants in responses. Please return one consistent casing (lowercase preferred) across all endpoints and never mix within a payload.
4.2 Get Current Pickups [LIVE]
GET /v1/pickups/getpickups?userid=1001&fromdate=&todate=&t= — the rider's active/in-progress stops. Same object shape as §4.1.
4.3 Get Pickups V3 (date-bounded / picked history) [LIVE]
GET /v3/pickups/getpickups?userid=1001&fromdate=&todate=&t= — used for completed/"picked" history. Same object shape.
Please clarify the intended difference between v1/v2/v3 of
getpickupsso we can consolidate. Ideally one endpoint filtered byorderstatusand date range.
4.4 Update Stop status [LIVE — needs delivery extension, see §4.5]
PUT /v1/pickups/updatepickup
This one endpoint is called at every state transition; orderstatus selects the transition. Common fields on all transitions:
{
"pickupid": 8890,
"orderheaderid": 4501,
"orderstatus": "<state>",
"riderslat": "12.9716", // rider GPS at the moment
"riderslon": "77.5946",
"raw_latitude": "12.9716", "raw_longitude": "77.5946",
"velocity_lat": "0", "velocity_lng": "0", "speed": "0", "heading": "0",
"notes": ""
}
Per-transition additional fields:
accepted — rider starts the assigned route/stop.
active — rider en route to the stop.
arrived — rider reached the stop (passes geofence check):
{ "orderstatus": "arrived", "arrivaltime": "2026-07-18 10:05:00", "pickuplat": "", "pickuplong": "", "actualkms": "", "pickupamt": 0.0 }
Picked up (pickup complete) — the big one:
{
"orderstatus": "Picked up",
"pickupedtime": "2026-07-18 10:07:00",
"pickuptime": "2026-07-18 10:07:00",
"pickuplocationid": 9,
"pickuplat": "12.9716", "pickuplong": "77.5946",
"riderkms": "0.4200", // distance rider travelled to this stop
"ridercharges": 2.10, // payout for this leg
"ridertime": 12, // minutes
"pickupamt": 0.0,
"collectionamt": 100.0, // amount due
"collectedamt": 100.0, // amount actually collected
"collectionstatus": "collected",
"smspickup": 0,
"wasskipped": false, // true if this stop had been skipped earlier
"bonuspts": 5,
"dropimage": "<base64-or-url>" // photo proof of pickup
}
skipped — rider skips this stop, will resume later (first-class; sequence preserved).
cancelled — pickup could not be completed (reason in notes).
rejected — rider rejects the assigned stop.
picked — internal "picked" marker (via updatepickup v1). Please clarify vs Picked up.
⚠️ Please normalize
orderstatusvalues. Today they are inconsistent ("Picked up"with a space & capital, vs"arrived","active","skipped"lowercase). Give us one canonical set of machine values (e.g. all lowercase snake:assigned,active,arrived,picked_up,delivered,skipped,cancelled,rejected) and we'll map the UI labels ourselves.
Response for all updates: standard envelope with status: true.
4.5 ⭐ REQUIRED CHANGE — Stop type (Pickup vs Delivery) [CHANGE]
This is the single most important change for go-live. Each stop on a route can be a pickup or a delivery, and the app UI must branch on it. Today the API returns no such field, so the app treats every stop as a pickup. Please add:
-
On every stop object (§4.1–4.3) add a stop type field:
"type": "pickup" // "pickup" | "delivery"(Name it
typeorstoptype— the app already looks fortype/stoptype/stopType; pick one and tell us.) -
For
deliverystops, the stop object must carry delivery details:Field Type Meaning dropaddressstring Where to deliver droplat/droplonstring Delivery coordinates (geofence + nav) otpstring/int Delivery OTP the customer gives (proof of delivery) collectionamtnumber COD to collect on delivery (0 = prepaid) customer name/phone string Recipient contact (reuse pickupcustomer/pickupcontactnoor give delivery-specific fields — tell us which) -
Delivery completion goes through the same
PUT /v1/pickups/updatepickupwith:{ "pickupid": 8891, "orderstatus": "delivered", // canonical value TBD (see §4.4 note) "deliveredtime": "2026-07-18 10:20:00", "otp": "4821", // OTP the rider entered — verify server-side "dropimage": "<base64-or-url>", // photo proof of delivery "collectedamt": 0.0, "collectionstatus": "prepaid", "riderslat": "…", "riderslon": "…", "riderkms": "…", "bonuspts": 5 }Please confirm: (a) whether OTP is verified server-side (recommended) or just recorded, (b) the canonical
orderstatusfor a completed delivery, (c) delivery-specific proof fields (signature? photo? OTP-only?).
4.6 Stop status lifecycle (state machine)
assigned ──► active ──► arrived ──► ┌─ (pickup) picked_up ─┐
│ │ │ └─ (delivery) delivered ─┤──► [next stop]
│ │ └────────────► skipped ──► (resume later) ─► active
└────────────┴──────────────────────► cancelled / rejected
Rules the backend must enforce/allow:
- Rider cannot reorder;
stepis authoritative. - Rider can skip any stop and resume it later —
skippedis not terminal. - After the last stop the rider returns to the origin hub. Please tell us whether hub-return is its own record/status or implicit.
4.7 Create Pickup Log [LIVE]
POST /v2/pickups/createpickuplog — audit/event log entries for a stop. Body is wrapped in an array: [ { ...event } ]. Please document the event schema (event type, timestamp, pickupid, lat/long).
5. Earnings / Summary
5.1 Partner summary [LIVE]
GET /v2/partners/... (base https://jupiter.doormile.app/live/api/v2/partners)
Powers the Earnings screen (daily/weekly/monthly totals, stop counts, payout). Please document the exact path + params (userid, period, date range) and the response fields (totals, per-day breakdown).
5.2 Rider weekly KMs [LIVE]
GET /v1/partners/... — weekly distance for payout. Document exact path + response.
6. Supporting endpoints
These are used by the app; please document each (request + response):
- Notifications — list rider notifications (used on the notifications tab). Need: list + mark-read.
- Rewards / bonus points — the app shows
bonuspts/bonusPoints; document how points are earned and fetched. - Support tickets — create ticket, list tickets (models exist: subject, description, status, timestamps).
- FCM push — confirm the payload schema for push notifications (new stop assigned, route updated, etc.) so we can handle taps/deep-links.
- App version / force-update — the app tracks
CurrentVersion; if you gate minimum version, document the endpoint.
7. Field glossary (canonical names)
| Field | Meaning |
|---|---|
userid |
Rider's user id (primary key the app sends everywhere) |
riderid / partnerid |
Rider/partner identifiers |
shiftid / logid |
Shift and current duty-log ids |
tenantid / locationid / applocationid |
Org / hub / app-location scoping |
pickupid |
Stop id — the PK for a single stop, used on all status updates |
orderid / orderheaderid |
Order + order-header identifiers |
step |
Stop's fixed position in the route (1..N) |
type / stoptype |
NEW: pickup | delivery |
orderstatus |
Stop state (see §4.6) |
collectionamt / collectedamt / collectionstatus |
COD due / collected / status |
pickuplat,pickuplong / droplat,droplon |
Stop / drop coordinates |
riderslat,riderslon |
Rider GPS at time of action |
riderkms / ridercharges / ridertime |
Distance / payout / minutes for a leg |
otp |
Delivery OTP (proof of delivery) |
dropimage |
Photo proof (pickup or delivery) |
bonuspts |
Bonus points for completing a stop |
pickupradius |
Geofence radius (m) for arrived/complete |
8. Open questions for the backend team (please answer in your returned doc)
- Auth model — token-based or
userid-only? (§1.2) - Single host + valid TLS + working DNS — can we drop the
queue.workolik.comhost and the SSL/IP hack? (§1.1) - Canonical
orderstatusvalues — give us the final machine strings. (§4.4) - Stop
typefield — final field name (typevsstoptype) and the delivery fields. (§4.5) - Delivery proof — OTP verified server-side? photo/signature required? canonical
deliveredstatus? (§4.5) - getpickups v1/v2/v3 — can we consolidate to one endpoint? (§4.3)
- Casing — confirm all-lowercase field names across every endpoint. (§4.1)
- Timezone — confirm IST for all timestamps. (§1.4)
- Hub return — is returning to hub its own status/record? (§4.6)
- Summary, rewards, notifications, support, push — full request/response schemas. (§5–6)
Generated from the current Miler app's live API integration. Every field marked [LIVE] is already sent/consumed by the app in production code — please preserve those names or tell us the new ones so we can migrate.