docs: jupiter to Doormile API migration map
Maps every jupiter endpoint we have replaced to its Doormile equivalent, for the express console and the miler app only. Marks which jupiter paths were confirmed from live network logs versus taken from the prior codebase analysis, and states per row whether the Doormile side has been hit with a real request or only compiles. Includes the 11-way decomposition of PUT /deliveries/updatedelivery, the behaviour changes that break a naive repoint, and the gaps jupiter covered that Doormile does not yet — per-site reporting being the notable one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
238
docs/jupiter2doormile.md
Normal file
238
docs/jupiter2doormile.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# jupiter → Doormile
|
||||
|
||||
What the old Nearle/jupiter API did, and what replaces it in Doormile. Two
|
||||
surfaces only — the **express console** and the **miler app**. Hub console, CRM
|
||||
and the B2C customer app are out of scope here.
|
||||
|
||||
Base URLs:
|
||||
|
||||
| | jupiter | Doormile |
|
||||
|---|---|---|
|
||||
| API | `jupiter.nearle.app/live/api/v1` | `api.doormile.com/api/v1` |
|
||||
| Write path | `queue.workolik.com` (TLS verify off, hardcoded IP pin) | same host, no side channel |
|
||||
|
||||
**Confidence marking.** Paths marked ✅ were read off real network logs from the
|
||||
live jupiter console. Paths marked ~ come from the prior-session analysis of the
|
||||
jupiter codebase and have not been re-confirmed against a live request — check
|
||||
the exact spelling before wiring anything to them.
|
||||
|
||||
Status: **Done** = built and hit with a real request · **Built** = written and
|
||||
compiled, never called · **Gap** = nothing replaces it yet · **Dropped** =
|
||||
deliberately not migrated.
|
||||
|
||||
---
|
||||
|
||||
## 1. Auth
|
||||
|
||||
| jupiter | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ~ console login (undocumented in jupiter's own API docs — found only by reading the console source) | `POST /admin/login` → `{email, password}` | **Done** |
|
||||
| ~ rider login | `POST /miler/login` then `POST /miler/verify-pin` | **Done** |
|
||||
|
||||
Two real differences:
|
||||
|
||||
- Doormile splits rider login into **phone → PIN**, two calls. jupiter did it in
|
||||
one.
|
||||
- The Doormile console token carries **`tenantid`**. jupiter had no tenant
|
||||
concept on the login at all; every console user saw everything. This is the
|
||||
single biggest behavioural change for a client account.
|
||||
- `configid` must be **1001** on both miler calls. There is no jupiter
|
||||
equivalent — it's a Doormile login partition.
|
||||
|
||||
---
|
||||
|
||||
## 2. Express console
|
||||
|
||||
### 2.1 Rider screens
|
||||
|
||||
| jupiter | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ✅ `GET /deliveries/getridersummary/?applocationid=&fromdate=&todate=` | `GET /admin/milers/summary?applocationid=&from=&to=&tenantid=&hubid=` | **Done** |
|
||||
| ~ rider list | `GET /admin/milers?applocationid=&hubid=&tenantid=` | **Done** |
|
||||
| ~ rider detail | `GET /admin/milers/:id` | **Done** |
|
||||
| ~ `riderlogs` (the 1.17M-row, zero-index table) | `GET /admin/milers/:id/logs?from=&to=&limit=` | **Done** |
|
||||
| ✅ `getriderlocationsummary` *(name confirmed, path inferred)* | covered by `milers/summary` (`currentlatitude/longitude`, `lastpingat`) and `milers/:id/logs` | **Done** |
|
||||
| — *(no jupiter equivalent)* | `GET /admin/milers/:id/activity?from=&to=` | **Done** |
|
||||
| ~ rider create/edit | `POST /admin/milers`, `PUT /admin/milers/:id` | **Done** |
|
||||
| ~ block rider | `PUT /admin/milers/:id/block` | **Built** |
|
||||
| ~ assign vehicle | `PUT /admin/milers/:id/assign-vehicle` | **Built** |
|
||||
| — | `POST /admin/milers/:id/notify` | **Done** |
|
||||
|
||||
Parameter translation: jupiter used `fromdate`/`todate`, Doormile uses
|
||||
`from`/`to`. Both `YYYY-MM-DD`. jupiter's `applocationid=0` meant "all cities";
|
||||
Doormile means the same by **omitting** the param.
|
||||
|
||||
### 2.2 Orders / deliveries
|
||||
|
||||
| jupiter | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ✅ `GET /deliveries/getdeliveries/` | `GET /admin/bookings` + `GET /admin/consignments` | **Done** |
|
||||
| ~ `getdelivery` / `getorders` | `GET /admin/bookings/:id`, `GET /admin/consignments/:id` | **Done** |
|
||||
| ~ `POST /deliveries/createdeliveries` | `POST /admin/expressbooking` | **Done** |
|
||||
| ~ `createdeliveries` in bulk | `POST /admin/expressbooking/bulk` (max 200, per-row results) | **Built** |
|
||||
| ~ `PUT /deliveries/updatedelivery` | **split into 11 endpoints** — see §4 | **Done / partial** |
|
||||
| — | `GET /admin/bookings/:id/track` | **Done** |
|
||||
| — | `GET /admin/consignments/:id/logs` | **Done** |
|
||||
| — | `GET /admin/consignments/track/:trackingno` | **Built** |
|
||||
|
||||
Two jupiter bugs that do not carry over, by construction:
|
||||
|
||||
- `getdeliveries` returned **every row 21×** (unconstrained `LEFT JOIN
|
||||
tenantpricing`, `DISTINCT` over 87 columns that deduped nothing). Doormile's
|
||||
list endpoints are paginated (`pageno`/`pagesize`, default 500, cap 1000) and
|
||||
return one row per booking.
|
||||
- `createdeliveries` had a quadratic insert bug — a slice declared outside the
|
||||
loop kept accumulating, producing ~2× duplicate `deliveryqueues` rows
|
||||
(66,446 deliveries → 132,826 rows, confirmed live). `createExpressBooking` is
|
||||
a single transaction per booking; `/bulk` loops it and reports per-row.
|
||||
|
||||
### 2.3 Reporting
|
||||
|
||||
| jupiter | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ✅ `GET /deliveries/getreportsummary/?applocationid=&tenantid=&locationid=&fromdate=&todate=` | `GET /admin/reports?from=&to=&tenantid=&hubid=` | **Done** |
|
||||
| — | `GET /admin/dashboard?tenantid=` | **Done** |
|
||||
|
||||
**One parameter has no equivalent yet: `locationid`.** jupiter could report per
|
||||
client *site* — per kitchen, for a food client. Doormile carries
|
||||
`pickuplocationid` on both the booking and the consignment, so the data is
|
||||
there, but `/admin/reports` does not group or filter by it. See §6.
|
||||
|
||||
### 2.4 Tenants and their sites
|
||||
|
||||
| jupiter | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ✅ `GET /tenants/gettenants/` | `GET /admin/tenants` | **Done** |
|
||||
| ✅ `GET /tenants/gettenantlocations/` | `GET /admin/tenants/:id/locations` | **Done** |
|
||||
| ~ `getlocations` / `getlocation` / `getlocationdetails` | same as above | **Done** |
|
||||
| ~ tenant create/edit | `POST /admin/tenants`, `PUT /admin/tenants/:id` | **Done** |
|
||||
| ~ location create/edit | `POST /admin/tenants/:id/locations`, `PUT /admin/tenantlocations/:id` | **Done** |
|
||||
| ~ `getbranches` | `GET /admin/hubs` — *jupiter "branches" ≈ Doormile hubs; verify this is the same concept before relying on it* | **Built** |
|
||||
| ~ `getlocationsummary` | **Gap** — no per-site rollup, same missing piece as `locationid` above | **Gap** |
|
||||
|
||||
Doormile adds `locationname` on a tenant location. jupiter identified a site by
|
||||
its address alone, which does not distinguish two branches on one street.
|
||||
|
||||
---
|
||||
|
||||
## 3. Miler app
|
||||
|
||||
jupiter's rider app drove almost everything through one overloaded endpoint.
|
||||
Doormile gives each action its own route.
|
||||
|
||||
| jupiter action | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ~ rider login | `POST /miler/login` + `POST /miler/verify-pin` | **Done** |
|
||||
| ~ PIN reset | `POST /miler/reset-pin` — **now admin-only**, see §5 | **Done** |
|
||||
| ~ location ping | `PUT /miler/location` | **Done** |
|
||||
| ~ availability toggle | `PUT /miler/availability` | **Done** |
|
||||
| ~ assignment list | `GET /miler/assignments`, `GET /miler/assignments/:id` | **Done** |
|
||||
| ~ accept | `POST /miler/assignments/:id/accept` | **Done** |
|
||||
| ~ reject | `POST /miler/assignments/:id/reject` | **Built** |
|
||||
| ~ rider logs write | `POST /miler/logs`, `POST /miler/status` | **Done** |
|
||||
| ~ per-delivery logs | `POST /miler/consignments/logs` | **Done** |
|
||||
| — | `POST /miler/duty/start`, `PUT /miler/duty/end`, `GET /miler/duty/current` | **Done** |
|
||||
| — | `POST /miler/breaks/start`, `PUT /miler/breaks/end` | **Done** |
|
||||
| — | `GET /miler/earnings` | **Done** |
|
||||
| — | `POST /miler/support`, `GET /miler/support` | **Built** |
|
||||
| — | `GET /miler/notifications` | **Done** |
|
||||
| — | `PATCH /miler/notifications/:id/read` | **Gap** — stub, persists nothing |
|
||||
|
||||
---
|
||||
|
||||
## 4. `PUT /deliveries/updatedelivery` — the 11-way split
|
||||
|
||||
This is the centre of the migration. jupiter overloaded one endpoint for **11
|
||||
distinct real actions**, distinguished only by which JSON fields happened to be
|
||||
non-empty. Each is now its own route with its own validation and its own status
|
||||
transition.
|
||||
|
||||
**Eight rider actions:**
|
||||
|
||||
| Action | Doormile |
|
||||
|---|---|
|
||||
| reached pickup | `POST /miler/bookings/:bookingid/reached` |
|
||||
| confirm parcel + dimensions | `POST /miler/bookings/:bookingid/parcel` |
|
||||
| collect payment | `POST /miler/bookings/:bookingid/payment` |
|
||||
| pickup complete | `POST /miler/bookings/:bookingid/pickup-complete` |
|
||||
| needs a bigger vehicle | `POST /miler/bookings/:bookingid/vehicle-required` |
|
||||
| cancel before pickup | `POST /miler/bookings/:bookingid/cancel` |
|
||||
| deliver | `POST /miler/consignments/:id/deliver` |
|
||||
| skip / failed attempt | `POST /miler/consignments/:id/skip` |
|
||||
|
||||
**Three console actions** that were bundled into the same rider endpoint:
|
||||
|
||||
| Action | Doormile |
|
||||
|---|---|
|
||||
| assign a rider | `POST /admin/bookings/:id/assign-miler` |
|
||||
| change status | `PUT /admin/bookings/:id/status` · `PUT /admin/consignments/:id/status` |
|
||||
| cancel | `POST /admin/bookings/:id/cancel` · `POST /admin/bookings/bulk-cancel` |
|
||||
|
||||
`pickup-complete` is the pivot the old system had no concept of: it converts the
|
||||
booking into a **consignment**, recomputes chargeable weight from the dimensions
|
||||
the rider entered, and decides routing — matching 3-digit pincode prefixes go
|
||||
straight to `Out_for_Delivery` (hyperlocal), everything else routes via a hub.
|
||||
|
||||
---
|
||||
|
||||
## 5. Behaviour changes that break a naive repoint
|
||||
|
||||
Response shapes are completely different — flat 87- and 92-column jupiter rows
|
||||
versus nested Doormile JSON. Every screen that parses a response needs
|
||||
rewriting, not repointing. Beyond that:
|
||||
|
||||
1. **Tenant scoping is real now.** A client console login sees only its own
|
||||
tenant. `?tenantid=` narrows for Doormile staff; a client passing another
|
||||
tenant's id gets **403**. Cross-tenant reads of a single resource return
|
||||
**404**, not 403, so ids aren't probeable. jupiter had none of this.
|
||||
2. **`configid` 1001** on every miler auth call. No jupiter equivalent.
|
||||
3. **PIN reset is admin-only.** jupiter let anyone reset a rider PIN with just a
|
||||
phone number, which is the login identifier, not a secret. Two calls took over
|
||||
any account. The rider app must not call `/miler/reset-pin` — route resets
|
||||
through ops.
|
||||
4. **Identity comes from the token, never the body.** jupiter's telemetry
|
||||
endpoints took `userid` from the request body. Doormile ignores it.
|
||||
5. **Telemetry lat/long/speed/battery are strings**, and
|
||||
`POST /miler/consignments/logs` takes a **bare JSON array**.
|
||||
6. **Delivery OTP is opt-in per tenant** (`Tenant.Requiredeliveryotp`), default
|
||||
off. Off for DailyGrubs. When on, it's verified server-side.
|
||||
7. **Dates**: `from`/`to`, not `fromdate`/`todate`. IST wall-clock throughout.
|
||||
8. **CityGate**: a booking's pickup pincode prefix must be an open city — `641`
|
||||
Coimbatore, `600` Chennai, `560` Bengaluru, `500` Hyderabad, `629` Nagercoil.
|
||||
jupiter had no such gate.
|
||||
|
||||
---
|
||||
|
||||
## 6. Gaps — jupiter did this, Doormile does not yet
|
||||
|
||||
| What | Detail |
|
||||
|---|---|
|
||||
| **Per-site reporting** | jupiter's `getreportsummary` took `locationid`. Doormile stores `pickuplocationid` on bookings and consignments but neither groups nor filters by it. For a food client this is "how many orders went out of which kitchen" — likely the first thing DailyGrubs asks for. |
|
||||
| `getlocationsummary` | No per-site rollup endpoint. |
|
||||
| **Route optimisation** | jupiter used external paid services (`routes.workolik.com`) for multi-stop sequencing. Nothing in Doormile replaces true stop-ordering. `HubBatchAssign` decides *who* gets a booking, not *what order* to run stops in. |
|
||||
| Notifications read-state | `PATCH /miler/notifications/:id/read` is a stub; no table exists. |
|
||||
| `riderkms` / `ridercharges` backfill | Populated on new deliveries only. Rows completed before 2026-08-06 read 0 and will not backfill themselves. |
|
||||
|
||||
---
|
||||
|
||||
## 7. Dropped on purpose
|
||||
|
||||
| What | Why |
|
||||
|---|---|
|
||||
| `/v1/substitutions` CRUD | Rider substitutions. Low traffic in the old system; Suriya's call. Revisit if it turns out to matter. |
|
||||
| jupiter's v2 endpoints | They wrote **only to Redis**, invisible to the v1/v3 Postgres reads — genuine split-brain, with a Redis `INCR` id space that could collide with the Postgres sequence. Doormile keeps Redis for ephemeral telemetry only; durable state is always Postgres. |
|
||||
| `queue.workolik.com` write path | Separate host with TLS verification disabled and a hardcoded IP pin. Not reproduced. |
|
||||
| ~20 never-populated columns on `orders`, 6 lat/lng pairs for 3 real points on `deliveries`, status spread across 6 text+timestamp column pairs | Replaced by a normalised schema with a real event-log table (`consignmenthistory`). |
|
||||
|
||||
---
|
||||
|
||||
## 8. What is not migrated at all
|
||||
|
||||
Nothing on the client side has moved. The rider Flutter app and
|
||||
`doormile_express_console` still call `jupiter.nearle.app`. Doormile having the
|
||||
endpoint does not mean traffic uses it.
|
||||
|
||||
Suggested order: pick one net-new console screen (the rider summary, or
|
||||
reports) and wire it to Doormile first — it replaces nothing live, so it is the
|
||||
cheapest real proof the cutover works. Then the higher-traffic screens
|
||||
(deliveries list, rider status updates), then the rider app.
|
||||
Reference in New Issue
Block a user