updates on the customer app api
This commit is contained in:
291
docs/customer-app-api-crisp.md
Normal file
291
docs/customer-app-api-crisp.md
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
# Doormile Customer App (`doormile_cx`) API — Quick Reference
|
||||||
|
|
||||||
|
> **Base URL:** `https://api.doormile.com/api/v1`
|
||||||
|
> **Namespace:** `/customer/*` | **Auth Role:** `9` (Customer) | **Data Envelope:** `{ "success": true, "data": { ... } }`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Global Conventions
|
||||||
|
|
||||||
|
| Aspect | Specification | Details / Rules |
|
||||||
|
|---|---|---|
|
||||||
|
| **Naming** | `camelCase` | All request & response JSON fields use camelCase. |
|
||||||
|
| **Timestamps** | Epoch milliseconds (UTC, `int64`) | Parse directly with `DateTime.fromMillisecondsSinceEpoch(ts)`. |
|
||||||
|
| **Identifiers** | Sequence-backed Feistel permutation | **Booking Reference:** `DM-482913`<br>**Tracking Number:** `DMX10482913` |
|
||||||
|
| **Headers** | `Authorization: Bearer <accessToken>`<br>`X-Client: doormile-cx/<version>+<build>`<br>`X-Platform: android | ios`<br>`Idempotency-Key: <uuid>` | • Required on all routes except pre-auth & catalogue.<br>• Client telemetry logged on every request.<br>• Idempotency supported on booking creation & OTP verification. |
|
||||||
|
| **Token Lifetime** | Access: `1 hour` \| Refresh: `60 days` | Refresh tokens rotate on every use. Replaying a revoked token revokes the entire chain. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Complete Endpoint Reference (28 Routes)
|
||||||
|
|
||||||
|
### 🔐 Authentication (Pre-Auth & Session)
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `POST` | `/customer/auth/otp/request` | ❌ | Request OTP via SMS/Email (returns uniform success regardless of account existence). |
|
||||||
|
| `POST` | `/customer/auth/otp/verify` | ❌ | Verify OTP, returns JWT tokens & customer profile. |
|
||||||
|
| `POST` | `/customer/auth/signup` | ❌ | Register new customer or treat existing phone as sign-in. |
|
||||||
|
| `POST` | `/customer/auth/refresh` | ❌ | Rotate refresh token to issue a new access token. |
|
||||||
|
| `POST` | `/customer/auth/logout` | ✅ | Revoke session (omit `refreshToken` to sign out everywhere). |
|
||||||
|
| `GET` | `/customer/auth/me` | ✅ | Fetch currently authenticated customer identity. |
|
||||||
|
|
||||||
|
### 📦 Serviceability & Catalogue
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `GET` | `/customer/serviceability/states` | ❌ | List serviceable states (`ETag` / 304 supported). |
|
||||||
|
| `GET` | `/customer/serviceability/states/:code/districts` | ❌ | List serviceable districts in state (`ETag` / 304). |
|
||||||
|
| `GET` | `/customer/pickup-slots` | ❌ | List today's & tomorrow's time slots with zone capacity (`ETag` / 304). |
|
||||||
|
| `GET` | `/customer/config/booking-limits` | ❌ | Get booking limits (`maxDestinations`, `maxPackages`, `maxCodAmount`). |
|
||||||
|
|
||||||
|
### 📍 Places & Geocoding
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `GET` | `/customer/places/search?q=:query&lat=:lat&lng=:lng` | ✅ | Place search / autocomplete (empty query returns recent/saved). |
|
||||||
|
| `GET` | `/customer/places/reverse-geocode?lat=:lat&lng=:lng` | ✅ | Reverse geocode coordinates to structured address. |
|
||||||
|
|
||||||
|
### 💰 Fare Estimation
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `POST` | `/customer/fare/estimate` | ✅ | Compute estimated fare band (`minRupees`–`maxRupees`) & route distance. |
|
||||||
|
|
||||||
|
### 🚚 Bookings & Tracking
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `POST` | `/customer/bookings` | ✅ | Create multi-destination pickup booking (`Idempotency-Key` supported). |
|
||||||
|
| `GET` | `/customer/bookings?limit=20&cursor=:cursor&status=active` | ✅ | Keyset paginated customer bookings list. |
|
||||||
|
| `GET` | `/customer/bookings/:reference` | ✅ | Get full booking detail & live tracking snapshot (`ETag` supported). |
|
||||||
|
| `POST` | `/customer/bookings/:reference/cancel` | ✅ | Cancel booking (allowed strictly before rider status `arrived`). |
|
||||||
|
| `PATCH` | `/customer/bookings/:reference/destinations/:index` | ✅ | Update recipient details on a pending destination stop. |
|
||||||
|
| `GET` | `/customer/orders/:trackingId` | ✅ | Look up single order/parcel status by tracking number. |
|
||||||
|
|
||||||
|
### 👤 Profile & Saved Locations
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `GET` | `/customer/profile` | ✅ | Get customer profile details. |
|
||||||
|
| `PUT` | `/customer/profile` | ✅ | Update customer name/email. |
|
||||||
|
| `GET` | `/customer/locations` | ✅ | List up to 10 saved delivery/pickup addresses. |
|
||||||
|
| `POST` | `/customer/locations` | ✅ | Save a new location. |
|
||||||
|
| `PUT` | `/customer/locations/:id` | ✅ | Update an existing saved location. |
|
||||||
|
| `DELETE` | `/customer/locations/:id` | ✅ | Delete a saved location. |
|
||||||
|
|
||||||
|
### 🔔 Devices & Push Notifications
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `POST` | `/customer/devices` | ✅ | Register FCM device token for milestone push updates. |
|
||||||
|
| `DELETE` | `/customer/devices/:token` | ✅ | Unregister device token on logout. |
|
||||||
|
|
||||||
|
### 🛠️ Ops QA Testing (Non-Production Only)
|
||||||
|
| Method | Endpoint | Auth | Description |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `POST` | `/customer/ops/bookings/:reference/stage` | ✅ | Double-gated test helper to walk a booking through stages. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Core Request & Response Payloads
|
||||||
|
|
||||||
|
### 1) OTP Verification (`POST /customer/auth/otp/verify`)
|
||||||
|
```json
|
||||||
|
// Request
|
||||||
|
{
|
||||||
|
"identifier": "+919876543210",
|
||||||
|
"otp": "1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response (200 OK)
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"accessToken": "eyJhbGciOi...",
|
||||||
|
"refreshToken": "d8f1e2a3...",
|
||||||
|
"expiresIn": 3600,
|
||||||
|
"customer": {
|
||||||
|
"id": 1042,
|
||||||
|
"name": "Alex Kumar",
|
||||||
|
"phone": "+919876543210",
|
||||||
|
"email": "alex@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) Fare Estimate (`POST /customer/fare/estimate`)
|
||||||
|
```json
|
||||||
|
// Request
|
||||||
|
{
|
||||||
|
"pickup": {
|
||||||
|
"latitude": 13.0827,
|
||||||
|
"longitude": 80.2707,
|
||||||
|
"stateCode": "TN",
|
||||||
|
"districtCode": "CHN"
|
||||||
|
},
|
||||||
|
"destinations": [
|
||||||
|
{
|
||||||
|
"stateCode": "TN",
|
||||||
|
"districtCode": "CHN",
|
||||||
|
"packages": [{ "weightKg": 2.5 }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stateCode": "KA",
|
||||||
|
"districtCode": "BLR",
|
||||||
|
"packages": [{ "weightKg": 1.0 }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response (200 OK)
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"minRupees": 240,
|
||||||
|
"maxRupees": 310,
|
||||||
|
"routeKm": 348.5,
|
||||||
|
"breakdown": {
|
||||||
|
"baseFare": 180,
|
||||||
|
"additionalStopsUplift": 60,
|
||||||
|
"estimatedTax": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3) Booking Creation (`POST /customer/bookings`)
|
||||||
|
```json
|
||||||
|
// Request
|
||||||
|
{
|
||||||
|
"slotId": "slot_20260908_t2",
|
||||||
|
"pickup": {
|
||||||
|
"title": "Home",
|
||||||
|
"sub": "Flat 4B, Green Towers, Anna Nagar",
|
||||||
|
"latitude": 13.0827,
|
||||||
|
"longitude": 80.2707,
|
||||||
|
"contactName": "Alex Kumar",
|
||||||
|
"contactPhone": "+919876543210"
|
||||||
|
},
|
||||||
|
"destinations": [
|
||||||
|
{
|
||||||
|
"recipientName": "Priya S",
|
||||||
|
"recipientPhone": "+919840123456",
|
||||||
|
"building": "12/A",
|
||||||
|
"street": "MG Road",
|
||||||
|
"landmark": "Near Metro",
|
||||||
|
"districtCode": "CHN",
|
||||||
|
"stateCode": "TN",
|
||||||
|
"latitude": 13.0850,
|
||||||
|
"longitude": 80.2100,
|
||||||
|
"packageCount": 1,
|
||||||
|
"codAmount": 450
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"remarks": "Handle with care"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response (201 Created)
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"reference": "DM-482913",
|
||||||
|
"stage": "booked",
|
||||||
|
"status": "active",
|
||||||
|
"cancellable": true,
|
||||||
|
"createdAt": 1788775499000,
|
||||||
|
"slotId": "slot_20260908_t2",
|
||||||
|
"pickup": {
|
||||||
|
"title": "Home",
|
||||||
|
"sub": "Flat 4B, Green Towers, Anna Nagar",
|
||||||
|
"latitude": 13.0827,
|
||||||
|
"longitude": 80.2707
|
||||||
|
},
|
||||||
|
"destinations": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"stateName": "Tamil Nadu",
|
||||||
|
"districtName": "Chennai",
|
||||||
|
"packageCount": 1,
|
||||||
|
"codAmount": 450,
|
||||||
|
"trackingId": null,
|
||||||
|
"stage": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Lifecycle & Stage Machine
|
||||||
|
|
||||||
|
### Stage Progression Sequence
|
||||||
|
```
|
||||||
|
[ booked ] ──► [ assigned ] ──► [ arrived ] ──► [ picked_up ] ──► [ order_created ]
|
||||||
|
│ (cancel window closes)
|
||||||
|
▼
|
||||||
|
[ delivered ] ◄── [ out_for_delivery ] ◄── [ in_transit ]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage Vocabulary
|
||||||
|
| Stage Key | Meaning / Trigger | Cancellable? | Scope |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| `booked` | Order submitted by customer | ✅ Yes | Booking |
|
||||||
|
| `assigned` | Rider assigned to visit | ✅ Yes | Booking |
|
||||||
|
| `on_the_way` | Rider accepted assignment | ✅ Yes | Booking |
|
||||||
|
| `arrived` | Rider arrived at pickup point (**cancellation cutoff**) | ❌ No | Booking |
|
||||||
|
| `picked_up` | Parcels collected and weighed | ❌ No | Booking |
|
||||||
|
| `order_created` | Tracking IDs generated per destination | ❌ No | Per Destination |
|
||||||
|
| `in_transit` | Parcels sorted / inwarded at hub | ❌ No | Per Destination |
|
||||||
|
| `out_for_delivery` | Dispatched with delivery agent | ❌ No | Per Destination |
|
||||||
|
| `delivered` | Successfully delivered to recipient | ❌ No | Per Destination |
|
||||||
|
| `cancelled` | Cancelled by customer or ops before arrival | — | Terminal |
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **Rollup Rule:** The overall booking stage reflects the **slowest order**. If Destination 1 is `delivered` but Destination 2 is `in_transit`, the booking rollup remains `in_transit`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Cross-App Impact & Compatibility
|
||||||
|
|
||||||
|
| Client / Component | Observable Change | Impact / Handling |
|
||||||
|
|---|---|---|
|
||||||
|
| **Miler App (Flutter)** | Multi-stop collection | Post-collection returns multiple stops sharing one `bookingid`. Keys must resolve by `consignmentid`. |
|
||||||
|
| **Admin Console** | Booking numbers & Search | Display format is now `DM-482913`. Searches match exact substring. |
|
||||||
|
| **Hub Console** | Tracking IDs & Inbound | Tracking numbers are now `DMX10482913`. |
|
||||||
|
| **Safety Mitigation** | `maxDestinations` Gate | Configured in DB (`customerbookinglimits.maxdestinations = 1`) to keep fanout single-destination until mobile updates deploy. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Standard Error Codes & Envelopes
|
||||||
|
|
||||||
|
All errors return JSON in standard format:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": {
|
||||||
|
"code": "SLOT_UNAVAILABLE",
|
||||||
|
"message": "That pickup time has passed — pick a new slot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| HTTP Status | Error Code (`error.code`) | Meaning / Recommended Client Action |
|
||||||
|
|---|---|---|
|
||||||
|
| `400` | `INVALID_INPUT` / `SLOT_EXPIRED` | Validation error or expired slot date. Prompt user to re-select. |
|
||||||
|
| `401` | `UNAUTHORIZED` | Token missing or expired. Redirect to OTP login / refresh session. |
|
||||||
|
| `403` | `FORBIDDEN` | Caller lacks role 9 customer access. |
|
||||||
|
| `404` | `NOT_FOUND` | Booking reference or tracking ID does not exist. |
|
||||||
|
| `409` | `SLOT_CAPACITY_FULL` | Slot filled up during checkout race. Prompt user to choose another time. |
|
||||||
|
| `409` | `BOOKING_NOT_CANCELLABLE` | Customer attempted cancel after rider `arrived`. Show un-cancellable alert. |
|
||||||
|
| `422` | `UNSERVICEABLE_PINCODE` | Location is outside active operating zones. |
|
||||||
|
| `429` | `RATE_LIMITED` | OTP requests exceeded limit (max 5/hour). Show countdown timer. |
|
||||||
|
| `500` | `INTERNAL_ERROR` | Generic server error (`X-Request-Id` logged). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Environment Variables & Deploy Flags
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GEOCODER_URL=https://nominatim.openstreetmap.org # Geocoding proxy
|
||||||
|
GEOCODER_EMAIL=ops@doormile.com # Nominatim contact policy
|
||||||
|
MILER_CALL_PROXY= # Set to proxy number in production (protects rider PII)
|
||||||
|
CX_STAGING_OTP=1234 # Fixed OTP for staging QA (disabled if ENV=production)
|
||||||
|
CX_ID_SCRAMBLE_KEY= # Optional custom key for Feistel sequence permutation
|
||||||
|
CX_ALLOW_STAGE_OVERRIDE=false # Double-gated QA stage override tool
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user