Files
doormile_milderapp/API_SPEC.md
2026-08-11 13:16:33 +05:30

401 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 require `Authorization: Bearer <token>` on every other call. If you keep `userid`-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):
```json
{
"code": 200,
"status": true,
"message": "Success",
"details": [ ... ] // object OR array — the actual payload
}
```
- The app reads the payload from `details` first, then falls back to `data`, then the root. **Please standardize on `details`.**
- On error return `status: false`, a non-2xx HTTP code, and a human-readable `message`.
### 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/false` or `1/0` — the app currently tolerates both for `status` but 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:
```json
{
"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:
```json
{
"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:
- `authmode` decides 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`
```json
{ "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:
```json
{
"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:
```json
{
"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`/`PickupLat` and `orderid`/`OrderId` variants 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 `getpickups` so we can consolidate. Ideally **one** endpoint filtered by `orderstatus` and 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:
```json
{
"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):
```json
{ "orderstatus": "arrived", "arrivaltime": "2026-07-18 10:05:00", "pickuplat": "", "pickuplong": "", "actualkms": "", "pickupamt": 0.0 }
```
**`Picked up`** (pickup complete) — the big one:
```json
{
"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 `orderstatus` values.** 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:
1. **On every stop object** (§4.1–4.3) add a stop type field:
```json
"type": "pickup" // "pickup" | "delivery"
```
(Name it `type` or `stoptype` — the app already looks for `type`/`stoptype`/`stopType`; **pick one and tell us**.)
2. **For `delivery` stops**, the stop object must carry delivery details:
| Field | Type | Meaning |
|---|---|---|
| `dropaddress` | string | Where to deliver |
| `droplat` / `droplon` | string | Delivery coordinates (geofence + nav) |
| `otp` | string/int | Delivery OTP the customer gives (proof of delivery) |
| `collectionamt` | number | COD to collect on delivery (0 = prepaid) |
| customer name/phone | string | Recipient contact (reuse `pickupcustomer`/`pickupcontactno` or give delivery-specific fields — **tell us which**) |
3. **Delivery completion** goes through the same `PUT /v1/pickups/updatepickup` with:
```json
{
"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 `orderstatus` for 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**; `step` is authoritative.
- Rider **can skip** any stop and resume it later — `skipped` is 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)
1. **Auth model** — token-based or `userid`-only? (§1.2)
2. **Single host + valid TLS + working DNS** — can we drop the `queue.workolik.com` host and the SSL/IP hack? (§1.1)
3. **Canonical `orderstatus` values** — give us the final machine strings. (§4.4)
4. **Stop `type` field** — final field name (`type` vs `stoptype`) and the delivery fields. (§4.5)
5. **Delivery proof** — OTP verified server-side? photo/signature required? canonical `delivered` status? (§4.5)
6. **getpickups v1/v2/v3** — can we consolidate to one endpoint? (§4.3)
7. **Casing** — confirm all-lowercase field names across every endpoint. (§4.1)
8. **Timezone** — confirm IST for all timestamps. (§1.4)
9. **Hub return** — is returning to hub its own status/record? (§4.6)
10. **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.*