From 511d369d7f6aca18cfa2f2eda8d6143a3cff0b5b Mon Sep 17 00:00:00 2001 From: Suriya Date: Thu, 6 Aug 2026 12:46:21 +0530 Subject: [PATCH] docs: refresh the express-console API reference Adds the five rider/tracking endpoints, the ?tenantid= staff filter and the 403-vs-404 refusal rules, and replaces the guesswork coverage note with what was actually run against production on 2026-08-06. Co-Authored-By: Claude Opus 5 --- docs/express-console-api.md | 146 ++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 22 deletions(-) diff --git a/docs/express-console-api.md b/docs/express-console-api.md index 37dbe95..247fd66 100644 --- a/docs/express-console-api.md +++ b/docs/express-console-api.md @@ -1,8 +1,10 @@ # Doormile Express Console — API reference -The console surface only (`/admin/*`). 89 routes: 1 login + 88 authenticated. +The console surface only (`/admin/*`). 94 routes: 1 login + 93 authenticated. Everything is under `https://api.doormile.com/api/v1`. +Verified against the build deployed 2026-08-06 12:41 IST. + Miler-app, hub-console, customer-app and CRM routes are not in this document. --- @@ -30,10 +32,28 @@ gets 401. | `0` / null | Doormile's own staff | everything, all tenants | | set (e.g. `13`) | a client's console login | only that tenant's rows | -Scoping is applied server-side. A client login **cannot** widen it by sending -`?tenantid=` or a body `tenantid` — on writes the server overwrites the field -with the token's tenant. Build the UI as if the API returns exactly what the -account is allowed to see, because it does. +Scoping is applied server-side. Build the UI as if the API returns exactly what +the account is allowed to see, because it does. + +**`?tenantid=` — the staff-only filter.** Doormile staff pass it to narrow any +of the list/summary endpoints to one client: + +``` +GET /admin/milers?tenantid=13 → DailyGrubs' 5 riders +GET /admin/dashboard?tenantid=13 → DailyGrubs' counters +GET /admin/reports?tenantid=13&from=…&to=… +``` + +Supported on `dashboard`, `reports`, `milers`, `milers/summary`, `customers`, +`bookings`, `consignments`. Omit it for the whole network. + +A **client** login passing another tenant's id gets **403** — not their own data +silently relabelled. Passing their own id, or omitting it, works normally. + +Reading one resource by id that belongs to another tenant returns **404**, not +403, so ids outside your own fleet aren't probeable. This covers bookings, +consignments and riders, on reads *and* writes — `POST /admin/bookings/:id/ +assign-miler` on someone else's booking is refused before the write, not after. Note the group is registered under a stale `// all open, no token required` comment in `routes.go`; the comment is wrong, `AuthMiddleware` is applied. @@ -133,16 +153,21 @@ Nagercoil is 5; read the rest from `GET /admin/hubs` rather than hardcoding. ## Milers (riders) -| Method | Path | Body | +| Method | Path | Body / notes | |---|---|---| -| GET | `/admin/milers` | tenant-scoped | +| GET | `/admin/milers` | tenant-scoped; `?applocationid=&hubid=&tenantid=` | +| GET | `/admin/milers/summary` | **the roster table** — see below | | POST | `/admin/milers` | see below | -| GET | `/admin/milers/:id` | | +| GET | `/admin/milers/:id` | 404 outside your fleet | +| GET | `/admin/milers/:id/logs` | GPS trail — see below | +| GET | `/admin/milers/:id/activity` | one rider's detail — see below | | PUT | `/admin/milers/:id` | | | PUT | `/admin/milers/:id/block` | | -| PUT | `/admin/milers/:id/assign-vehicle` | | +| PUT | `/admin/milers/:id/assign-vehicle` | `{ vehicleid }` | | POST | `/admin/milers/:id/notify` | `{ title, message }` — `:id` is the **milerprofileid** | +Every `:id` here is the **milerprofileid**, not the userid. + ```jsonc POST /admin/milers { @@ -160,6 +185,44 @@ POST /admin/milers } ``` +### The rider screens + +These are the express-console equivalents of jupiter's `getridersummary` and +its rider/delivery logs. + +**`GET /admin/milers/summary?from=&to=&applocationid=&hubid=&tenantid=`** + +One row per rider — live state on the left, range totals on the right. Defaults +to today. This is the rider list screen. + +```jsonc +{ "milerprofileid": 78, "userid": 41, "displayname": "Murali P", + "phone": "…", "availabilitystatus": "Offline", "defaultvehicletype": "Bike", + "hubid": null, "hubname": "", "rating": 5, + "onduty": false, "dutystartedat": null, + "currentlatitude": 11.0163, "currentlongitude": 77.0147, + "lastlocationupdatedat": "…", "lastpingat": "2026-08-05T18:10:00Z", + "assigned": 0, "accepted": 0, "rejected": 0, + "completed": 1, "cancelled": 0, "delivered": 1, + "riderkms": 0, "ridercharges": 0 } +``` + +`lastpingat` comes from Redis telemetry, `lastlocationupdatedat` from the +profile — they differ, and the first is the better staleness signal. + +**`GET /admin/milers/:id/logs?from=&to=&limit=`** + +The GPS trail. `limit` defaults to 500, caps at 5000. Returns +`{ data: [MilerLog…], total, distancekm, miler: {…}, from, to }` where +`distancekm` is the haversine sum over consecutive points. Telemetry coords are +**strings**, since that's what the rider app sends. + +**`GET /admin/milers/:id/activity?from=&to=`** + +One rider's detail page: `assignments[]`, the `bookings[]` behind them, +`dutylogs[]`, `breaklogs[]`, `lastpingat`, and `totals: { assignments, +delivered, riderkms, ridercharges, dutyminutes }`. + ## Bookings | Method | Path | Notes | @@ -167,7 +230,8 @@ POST /admin/milers | GET | `/admin/bookings` | tenant-scoped list | | POST | `/admin/expressbooking` | create one — passes CityGate | | POST | `/admin/expressbooking/bulk` | `{ "bookings": [ ... ] }`, max 200, per-row results | -| GET | `/admin/bookings/:id` | 403 if outside your tenant | +| GET | `/admin/bookings/:id` | 404 if outside your tenant | +| GET | `/admin/bookings/:id/track` | **the tracking screen** — see below | | POST | `/admin/bookings/:id/assign-miler` | | | POST | `/admin/bookings/:id/assign-vehicle` | | | PUT | `/admin/bookings/:id/status` | | @@ -208,14 +272,42 @@ Rules worth knowing: - Auto-assignment fires after commit as a background retry loop (5 attempts, 2 min apart). The response returns before a rider is attached. +### Tracking one booking end to end + +**`GET /admin/bookings/:id/track`** — one call for the whole lifecycle, so the +tracking screen doesn't have to stitch five requests together: + +```jsonc +{ + "booking": { …with parcels, serviceoptions, payments… }, + "assignments": [ { mileruserid, assignmentstatus, assignedat, acceptedat, + completedat, riderkms, ridercharges } ], // every attempt + "riders": [ { userid, displayname, phone, … } ], // one per attempt + "livelocation": { "latitude": …, "longitude": … }, // null if no ping in 30 min + "lastpingat": "…", + "consignment": { … }, // present once picked up + "history": [ { eventstatus, remarks, createdat } ], + "telemetry": [ ConsignmentLog… ], + "deliveryproof": { deliveredtoname, photourl, geolatitude, … } +} +``` + +`assignments` holds **every** attempt, not just the current one — a rejected +first assignment is exactly what ops needs when asking why a pickup was slow. + ## Consignments -| Method | Path | -|---|---| -| GET | `/admin/consignments` | -| GET | `/admin/consignments/:id` | -| GET | `/admin/consignments/track/:trackingno` | -| PUT | `/admin/consignments/:id/status` | +| Method | Path | Notes | +|---|---|---| +| GET | `/admin/consignments` | `?tenantid=` for staff | +| GET | `/admin/consignments/:id` | | +| GET | `/admin/consignments/:id/logs` | event history + telemetry + proof | +| GET | `/admin/consignments/track/:trackingno` | | +| PUT | `/admin/consignments/:id/status` | | + +`/:id/logs` returns `{ consignment, history[], telemetry[], deliveryproof? }` — +the durable Postgres event log and the rider's Redis trail in one response, +rather than making the console reconcile two stores. ## Tripsheets (hub-to-hub transport) @@ -283,10 +375,20 @@ Rules worth knowing: ConsignmentException, DoormilePricing, CarrierPricing. Partner and Tenant are hard-deleted. -## Not exercised yet +## Verified against production, 2026-08-06 -Roughly half these routes have never had a real request against them. Exercised -end-to-end so far: login, dashboard, reports, tenants + locations, milers -(create/list/notify), expressbooking (single), bookings list/detail, -assign-miler, consignments, profile password. Treat the rest as written but -unproven. +Run as both a client login (`info@dailygrubs.com`, tenant 13) and Doormile +staff (`developer@doormile.com`, tenant 0): + +- dashboard, reports, milers, milers/summary, milers/:id, milers/:id/logs, + milers/:id/activity, customers, bookings, bookings/:id, bookings/:id/track, + consignments, consignments/:id/logs, tenants, tenants/:id/locations +- `?tenantid=` narrowing on all of the above, for staff +- 403 on a client requesting another tenant; 404 on cross-tenant reads *and* + on `assign-miler` / `status` / `cancel` writes, with the target row confirmed + unmodified afterwards + +Still unproven: tripsheets, exceptions, vehicles, competitor-branches, +carrier-pricing, doormile-pricing, app-users CRUD, partner CRUD, bulk booking +create/cancel, `assign-vehicle`, `block`. Written, compiled, never called with +a real request.