Files
doormile_backend/docs/test-booking-runbook.md
Suriya 588f84c344 docs: runbook for creating a test booking and assigning a miler
Captures the working request bodies for the customer login/booking and the
admin/hub assign-miler endpoints, plus the constraints that are easy to hit
and hard to diagnose:

- CityGate only accepts pickup pincodes in 641/600/560/500
- hub assign-miler 403s unless milerprofiles.hubid matches the calling hub,
  and most milers have hubid NULL
- CreateCustomerBooking auto-assigns in the background, so a booking is often
  already on a miler before you assign one
- the routing watchdog sweeps unaccepted assignments within ~a minute and may
  re-assign under a new bookingassignmentid, so accept IDs must be re-read
- reject takes its reason in the body, not the query string

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 12:38:05 +05:30

8.3 KiB

Creating a test booking and assigning it to a miler

How to create a pickup booking through the real customer API and put it on a specific miler, against https://api.doormile.com. Every call below is the production API — nothing here is a test harness or a shortcut.

Base URL: https://api.doormile.com/api/v1


1. Log in as a customer (two steps)

Customer auth is phone lookup, then PIN. The first call only confirms the account exists; it returns no token.

curl -X POST https://api.doormile.com/api/v1/customer/login \
  -H 'Content-Type: application/json' \
  -d '{"phone":"9876543222"}'
{ "success": true, "message": "PIN verification required", "phone": "9876543222" }
curl -X POST https://api.doormile.com/api/v1/customer/verify-pin \
  -H 'Content-Type: application/json' \
  -d '{"phone":"9876543222","pin":"1234"}'

The token is at the top level as tokennot under data.

{ "success": true, "token": "eyJhbGciOi...", "user": { "appcustomerid": 1, "firstname": "Rahul", ... } }

Save it: TOKEN=eyJhbGciOi...

Customer test logins (all PIN 1234)

City Name Phone appcustomerid
Coimbatore Rahul Sharma 9876543222 1
Hyderabad Test Customer HYD 9999900001 7
Bangalore Test Customer BLR 9999999999 6

2. Create the booking

curl -X POST https://api.doormile.com/api/v1/customer/bookings \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "pickupaddress": "123 Gandhipuram Main Rd, Coimbatore",
    "pickuppincode": "641012",
    "pickuplatitude": 11.0168,
    "pickuplongitude": 76.9558,
    "deliveryaddress": "45 Saravanampatti, Coimbatore",
    "deliverypincode": "641035",
    "deliverylatitude": 11.0780,
    "deliverylongitude": 77.0010,
    "serviceoption": "Normal",
    "parcels": [{
      "itemcategory": "Documents",
      "itemdescription": "Test parcel",
      "declaredvalue": 500,
      "weight": 1.5,
      "length": 20, "width": 15, "height": 10
    }]
  }'

Returns the created booking under data, with bookingid and status Pending_Pickup. Note the bookingid — step 3 needs it.

Required vs optional

Only pickupaddress, pickuppincode and a non-empty parcels array are enforced. Delivery fields are optional; if you give a deliverypincode but no delivery lat/lon, the server geocodes the pincode for you. Parcel weight and dimensions are optional too — the miler weighs at pickup — but supplying them makes the volumetric-weight and pricing path realistic.

serviceoption is one of Normal, Fast, Superfast.

The pickup pincode must be in an operating city

CityGateMiddleware rejects anything else with {"code":"CITY_NOT_SUPPORTED"}. Allowed 3-digit prefixes:

Prefix City
641 Coimbatore
600 Chennai
560 Bengaluru
500 Hyderabad

Same-prefix pickup and delivery goes hyperlocal

If pickup and delivery share the first 3 digits, BookingPickupComplete sends the consignment straight to Out_for_Delivery and the same miler does the final mile. Different prefixes park it at Inwarded_at_Hub, which currently has no automated path onward. Keep both pincodes in the same prefix unless you are specifically testing the cross-hub gap.


3. Assign it to a specific miler

The AI routing layer usually gets there first. CreateCustomerBooking kicks off AssignCustomerMiler in a background goroutine, so a booking is often auto-assigned within seconds of creation. Check before assigning manually — you may already have what you want, on a miler you didn't pick.

Admin console — works for any miler

curl -X POST https://api.doormile.com/api/v1/admin/bookings/26/assign-miler \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"mileruserid": 4}'

Admin token comes from POST /admin/login (roles 1, 3, 4).

This path has no hub check and no city check — it will put any miler on any booking. That is what makes it the reliable option.

Hub console — miler must belong to the calling hub

curl -X POST https://api.doormile.com/api/v1/hub/bookings/26/assign-miler \
  -H "Authorization: Bearer $HUB_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"mileruserid": 4}'

Two extra constraints, both common sources of confusion:

  • The booking must be in an assignable state, else 400 booking is not in an assignable state.
  • The miler's milerprofiles.hubid must equal the hub the token belongs to, else 403 miler does not belong to this hub. Most milers in production have hubid = NULL and therefore cannot be assigned here at all — they are also invisible in the hub console's miler list. Use the admin endpoint, or set a hubid first.

Both endpoints take the same body and funnel into the same AssignMilerToBooking function, so the resulting DB state is identical: booking → Miler_Assigned, a new bookingassignments row with status Assigned, and the miler's availabilitystatusAssigned.


4. Accept it as the miler — within about a minute

A watchdog on the AI routing layer reverts assignments that are not accepted promptly: the assignment row is deleted and the booking goes back to Created. Observed turnaround is under a minute.

In practice it often re-assigns rather than leaving the booking idle, so the booking still looks assigned to the same miler — but under a new bookingassignmentid. Observed on booking 26: assignment 42 was swept and replaced by 43 within ~2 minutes, same miler, same booking. So never hardcode an assignment ID you noted earlier; always re-read GET /miler/assignments immediately before accepting, or the accept will 404.

# Miler login mirrors the customer flow
curl -X POST https://api.doormile.com/api/v1/miler/login \
  -H 'Content-Type: application/json' -d '{"phone":"9876543255"}'

curl -X POST https://api.doormile.com/api/v1/miler/verify-pin \
  -H 'Content-Type: application/json' \
  -d '{"phone":"9876543255","pin":"1234"}'

# List what is actually actionable, then accept
curl -H "Authorization: Bearer $MILER_TOKEN" \
  https://api.doormile.com/api/v1/miler/assignments

curl -X POST https://api.doormile.com/api/v1/miler/assignments/42/accept \
  -H "Authorization: Bearer $MILER_TOKEN"

GET /miler/assignments only returns assignments in Assigned or Accepted, and accept only succeeds on one currently Assigned — a completed or rejected job can no longer be revived.

To reject instead, the reason goes in the body, not the query string:

curl -X POST https://api.doormile.com/api/v1/miler/assignments/42/reject \
  -H "Authorization: Bearer $MILER_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"reason":"too far"}'

5. Rest of the delivery flow

In order, all as the miler, all Authorization: Bearer $MILER_TOKEN:

Step Call
Arrived at customer POST /miler/bookings/:bookingid/reached
Confirm parcels POST /miler/bookings/:bookingid/parcel-confirm
Pickup complete POST /miler/bookings/:bookingid/pickup-complete
Deliver POST /miler/consignments/:consignmentid/deliver

pickup-complete is what converts the booking into a consignment, sets the origin hub from the miler's own hubid, and applies the hyperlocal rule from step 2.


Miler reference

Milers with hubid = NULL work fine via the admin endpoint but not the hub console. Verify the current roster before relying on this table — production has ~24 milers and the seed data has been reshaped more than once.

userid Name Phone Hub City
4 Ramesh Kumar 9876543255 1 — Jupiter Coimbatore
23 Karthikeyan Vel 9876500003 1 — Jupiter Coimbatore
6 Rajesh Sekhar 9876543256 2 — Neptune Coimbatore
7 Karthi Keyan 9876543257 7 — RS Puram Coimbatore
21 Murugan Palani 9876500001 8 — Saravanampatti Coimbatore
22 Selvam Raja 9876500002 17 — Test Hub Coimbatore
8 Suresh Goud 9876543261 NULL Hyderabad
28 Suresh Naik 9876500008 NULL Bangalore

Coimbatore hubs: 1 Jupiter (641012, sorting centre), 2 Neptune (641004), 7 RS Puram (641002), 8 Saravanampatti (641035), 17 Test Hub (641099).

All miler PINs above are 1234.