# Handoff — after backend-phase-7 (Search & matching) **Search is live.** Verified nurses are now discoverable through one public endpoint backed by a denormalized, maintained-on-write index. This unblocks **frontend f6-b7**: search + filters (C1), results list (C2), and the nurse profile (C3) can be built against a real API. ## What the frontend can now build (f6-b7) - **Search + filters (C1)** → `GET api/v1/search/nurses` (public, no auth). Query params (snake_case): `service_category_id` (**required**), `city_id` (**required**), `district_id` (optional), `nurse_gender` (`male`/`female`, optional), `min_price`/`max_price` (IRR long, optional), `price_unit` (optional), `page`/`page_size` (default 1 / 50, max 100). - **Results list (C2)** → the `data` is a `PagedResult` (`items`, `total`, `page`, `pageSize`). Each item: `variantId`, `nurseId`, `serviceCategoryId`, `price` (IRR **digit string**), `priceUnit`, `nurseGender`, `averageRating`, `totalReviews`, `totalCompletedBookings`, `cityId`, `districtId` (null = whole city). - **Nurse profile (C3)** → reuse the b6 public trust badge (`GET api/v1/nurses/{id}/trust_badge`) and the b5 public variant read (`GET api/v1/nurse_variants/get/{id}`) already live; b7 adds no new profile route. Categories/cities/districts for the filter dropdowns come from the **b4** geo lookups (`geo/*`) and **b5** catalog (`catalog/*`) — unchanged. ## Rules the UI must respect - **Only searchable nurses come back.** The backend returns a nurse **only** when verified + not suspended + accepting + variant active. No client-side re-check needed; an empty page is a valid result. - **`districtId = null` = whole city.** A city-only search returns both whole-city and district rows; a district search returns that district's rows **plus** whole-city rows. Show whole-city hits as covering the district the user searched. - **Same-gender filter is first-class.** Surface `nurse_gender` prominently; never default it silently. (Carrying the chosen gender into the booking request — `required_caregiver_gender` — is **b8**, not here.) - **`price` is an IRR digit string** — render with a formatter; never parse to a float. Combine with `priceUnit` (+ `sessionCount` from the variant, when booking) for the engagement total. - **Sort is rating-desc only** (MVP). No client sort options beyond what the API returns. ## Contracts - New: [`dev/contracts/domains/search.md`](../../contracts/domains/search.md). - `swagger.v1.json` refreshed (adds `search/nurses` + `admin_search/rebuild_index` + the DTOs). ## Backend notes (not frontend-facing) - The index is a **read-only projection** maintained inline inside each source write's transaction (`ISearchIndexMaintainer`, wired into the b3/b4/b5/b6 handlers). The read seam is **`INurseSearch`** (SQL now; Elasticsearch is a config-selected drop-in later, `Search:Backend`). - Admin `POST api/v1/admin_search/rebuild_index` (dynamic-permission) does an idempotent full rebuild — the reconciliation path; incremental maintenance and rebuild converge. - **Deferred to b8:** `booking_requests.required_caregiver_gender` capture (carry the chosen gender into the booking). **Deferred:** Elasticsearch backend + feeder, availability hard-filter, map/radius discovery, ranking beyond rating.