# booking-requests — the money-free pre-payment request > Client seam `client/src/services/bookingRequests/` · `USE_BOOKING_REQUESTS_MOCK = false` (**real**) · 7 server ops > Last verified: 2026-07-30 against commit `d3ec723` and swagger.v1.json (2026-07-29). Stage one of the booking flow: the customer asks, the nurse answers, and **no money exists yet**. A `booking_requests` row becomes a `bookings` row only on payment capture — see [bookings.md](bookings.md) and [payment.md](payment.md). > `Features/Booking` (singular, this domain) and `Features/Bookings` (plural, the post-payment engine) are > **different server areas, not a rename.** ## Endpoints | Method | Path | Caller | Verdict | | --- | --- | --- | --- | | POST | `/api/v1/booking_requests/create` | customer | wired | | GET | `/api/v1/booking_requests/list` | both | wired · paginated · role-scoped | | GET | `/api/v1/booking_requests/get/{id}` | both | wired | | POST | `/api/v1/booking_requests/accept/{id}` | nurse | wired | | POST | `/api/v1/booking_requests/reject/{id}` | nurse | wired | | POST | `/api/v1/booking_requests/cancel/{id}` | customer | wired | | GET | `/api/v1/booking_requests/checkout_summary/{id}` | customer | wired — but read by the **[payment](payment.md)** domain, not this one | All `[Authorize]`. No phantoms. `checkout_summary` is the C6 money read (gross / commission / VAT breakdown, REQ-016 delivered). It lives on this controller because the request is what gets paid for, and is documented in [payment.md](payment.md) where it is consumed. ## The lifecycle ``` pending_nurse_response ──accept──▸ accepted_awaiting_payment ──capture──▸ converted │ │ ├──reject──▸ rejected_by_nurse └──window lapses──▸ payment_deadline_expired ├──deadline──▸ expired_no_response └──customer──▸ cancelled_by_customer ``` **Forward-only.** A backward or sideways transition is a clean `409`, never a 500. Two deadlines are server-owned and config-driven (`booking_request_response_deadline_minutes`, `payment_window_minutes` in [admin.md](admin.md)): - the nurse's response window → `expired_no_response` - the customer's payment window after acceptance → `payment_deadline_expired` `POST /api/v1/admin_booking_requests/expire` is the ops one-shot for both; the in-process scheduler runs it unattended. See [admin.md](admin.md). ## Shape rules the JSON does not express - **Stage-one disclosure is deliberately partial.** Before payment the nurse sees only unencrypted `customerNotes` and a **city/district-coarse masked address**. Encrypted care instructions and the full address are unreadable until the booking is confirmed. This is a hard server rule, not a UI choice. - **The countdown is server-frozen.** `CountdownTimer` renders a deadline the server sent; the client never computes an expiry from a local clock. - **`variantPrice` is on `BookingRequestDto`** (REQ-013, delivered) so the customer sees the price they are committing to without a second variant fetch. It is a **digit string**. - **The inbox list item carries `variantLabel` + `patientAge`** (REQ-014, delivered). ### The two list shapes, exactly Confirmed field-by-field against the live swagger, because three in-repo comments disagree about this: | | `BookingRequestListItemDto` (list) | `BookingRequestDto` (detail) | | --- | --- | --- | | `variantLabel` | **yes** | yes | | `patientAge` | **yes** | — (`patientName` instead) | | `variantPrice` · `variantPriceUnit` | **no** | yes | | address / notes | `customerNotes` only | full masked address block | | `nurseRejectionReason` | — | yes, **free text** | Two consequences: - **`client/src/services/bookingRequests/types.ts` is behind the wire.** It marks `variantLabel` as "client-augmented … `undefined` on the real path", but the server serves it. Widening the client type is safe and would let the real inbox card render its decision-first headline today. - **REQ-050 is partly stale.** It states the list DTO carries neither field, "confirmed against `services/bookingRequests/types.ts`" — i.e. confirmed against the client type, not the wire. What the wire genuinely lacks is `variantPrice`/`variantPriceUnit` on the *list* row and the `status=answered` group filter. - **`requiredCaregiverGender` is never defaulted or dropped.** `any` is an explicit choice, distinct from absent. - The address the request references is snapshotted at create time; later edits to the saved address do not rewrite it. ## Enums | Vocabulary | Values | | --- | --- | | `BookingRequestStatus` | `pending_nurse_response` `accepted_awaiting_payment` `converted` `rejected_by_nurse` `expired_no_response` `payment_deadline_expired` `cancelled_by_customer` | | `RequiredCaregiverGender` | `male` `female` `any` | | `RequestRole` *(client-side list filter)* | `customer` `nurse` | Verified identical to `Baya.Domain/Entities/Booking/BookingRequestStatus.cs`. REQ-015 confirmed these serialise as the exact snake_case codes. ## Open REQs | REQ | Status | Effect | | --- | --- | --- | | REQ-044 | open | No structured `nurseRejectionReasonCode` — the wire carries free-text `nurseRejectionReason`. The client runs a keyword heuristic over it, documented in-code as a known approximation | | REQ-050 | open, **narrower than filed** | The list row already has `variantLabel`; it lacks `variantPrice`/`variantPriceUnit`. The `status=answered` group filter is genuinely absent, so the «پاسخ‌داده» tab fires three page-1 queries and concatenates — an unpaged workaround a nurse with many answered requests will hit |