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 <noreply@anthropic.com>
This commit is contained in:
Suriya
2026-08-06 12:46:21 +05:30
parent f44e8fa3b4
commit 511d369d7f

View File

@@ -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.