# After backend-phase-3 — profiles, patients & nurse bank accounts are live On top of the b2 auth spine, the *people behind the accounts* now exist. A nurse has a seller profile (that b6 will verify and b5 will hang service variants off), a customer has a payer profile and their patients, and a nurse has payout bank accounts with an automated IBAN-ownership inquiry. Contract: [`dev/contracts/domains/identity-profiles.md`](../../../contracts/domains/identity-profiles.md); machine schema: `dev/contracts/openapi/swagger.v1.json` (refreshed). ## What the frontend (f2-b3) can now build - **Nurse profile bootstrap:** `POST api/v1/nurse_profiles/upsert` (bio, experience, education, `specializationsJson`), `GET api/v1/nurse_profiles/me`, and the pause/resume toggle `POST api/v1/nurse_profiles/set_accepting_bookings`. `isVerified` and the rating/booking aggregates are **read-only** (verification is b6) — render them, never send them. - **Customer profile:** `POST api/v1/customer_profiles/upsert` (emergency contact) + `GET …/me`. - **"Who is care for" (patients):** `create` / `list` (paginated) / `get/{id}` / `update/{id}` / `archive/{id}` under `api/v1/patients`. `gender` (`male`/`female`) is **required**. A customer only ever sees/edits their own patients — someone else's id returns **404**. - **Nurse bank-account settings:** `add` (returns the account with `matchedNationalId` set by the شبا inquiry), `list` (IBAN **masked**, last-4), `set_primary/{id}`, and `verify_ownership/{id}` to re-run the inquiry. ## Rules baked into the API (don't fight them client-side) - **Refresh after `select_role`.** These endpoints authorize on the **role claim in the access token**; the token minted before role selection lacks it. Login → `select_role` → **refresh** (or re-login) → then call profile/patient/bank endpoints. Otherwise you get `403`. - **Guarded verification** — no field/endpoint sets `isVerified`; a nurse is not bookable until b6. - **Tenancy** — patients and bank accounts are strictly owner-scoped; cross-tenant reads/writes are `404`. - **IBAN is masked on the wire** (last-4 only); the full value is encrypted at rest. - **`matchedNationalId`** is the money-mule-prevention gate for the first payout (enforced in b13). It is `null` until the inquiry runs; `add` runs it automatically. The bank rail is **mocked** at MVP. - Duplicate IBAN → a clean `400` (via `iban_hash` uniqueness), not a server error. ## What's mocked - **IBAN ownership (`IBankAccountOwnershipVerifier` → 🟡).** Deterministic fake استعلام شبا: every IBAN matches except the configured mismatch IBAN (`Seams:BankOwnership:MismatchIban`, default `IR000000000000000000000000`) which returns `matchedNationalId=false`. No real bank/KYC call. ## Schema / migration Migration **`20260702042131_IdentityProfilesPatientsBankAccounts`** (applied to the dev DB on startup): `usr.NurseProfiles`, `usr.CustomerProfiles`, `usr.Patients`, `usr.NurseBankAccounts` — with the 1:1 uniques, `UNIQUE(iban_hash)`, filtered single-primary index, guarded `is_verified`, encrypted PII columns (`iban`, `account_holder_name`, emergency contacts, `initial_medical_notes`), and soft-delete on `NurseProfiles`. None of the CUT columns (`verification_status`, `response_rate`, … , `customer_profiles.national_id_verified_at`) exist. ## Deferred to later phases (do not build against these yet) - **Addresses & nurse service areas → b4** (need province/city/district + geocoder). - **`is_verified` flip → b6** (verification pipeline). - **Payout gating on `matched_national_id` → b13.** - **Aggregate recompute (rating/reviews/completed) → b9/b14.** - **Customer national-ID KYC** — intentionally not collected; never gate browsing/booking on it. ## Note for the whole backend chain FluentValidation was previously inert (no validators registered). b3 activates it in `AddApplicationServices` — every `AbstractValidator` now runs via `ValidateCommandBehavior` and the `ModelStateValidationAttribute` controller filter. **Consequence:** for route-supplied ids, don't add a body validator rule on that id (e.g. `patients/update/{id}` validates the body, whose `Id` is 0).