# nurse — nurse bank accounts

> Client seam `client/src/services/nurse/` · `USE_NURSE_BANK_MOCK = false` (**real**) · 4 server ops
> Last verified: 2026-07-30 against commit `d3ec723` and swagger.v1.json (2026-07-29).

The nurse's payout destination. Narrow domain, high stakes: a payout cannot be paid to an unverified IBAN.

> The domain is named `nurse`, not `nurse-bank-accounts`, because that is the client folder name. The
> nurse's *profile* lives in [profiles.md](profiles.md); coverage in
> [service-areas.md](service-areas.md); verification in [verification.md](verification.md).

## Endpoints

| Method | Path | Rate limit | Verdict |
| --- | --- | --- | --- |
| GET | `/api/v1/nurse_bank_accounts/list` | — | wired |
| POST | `/api/v1/nurse_bank_accounts/add` | `sensitive` 20/min | wired |
| POST | `/api/v1/nurse_bank_accounts/set_primary/{id}` | — | wired |
| POST | `/api/v1/nurse_bank_accounts/verify_ownership/{id}` | `sensitive` 20/min | wired |

All `[Authorize]`. No phantoms. The domain maps 1:1.

## Shape rules the JSON does not express

- **`iban_hash` is UNIQUE across the platform.** The same IBAN cannot be registered by two nurses; the
  second `add` returns a `409`. The uniqueness is enforced on a deterministic hash, not the encrypted
  column.
- **The IBAN is encrypted at rest and returned masked** — `maskedIban` on every read model, including the
  admin-side `PayoutDto` and the nurse's own `NursePayoutHistoryDto`. **The full IBAN is never returned
  after the write that created it.** Write-then-masked is the pattern.
- **`verify_ownership` is استعلام شبا** — a Shahkar-class inquiry that confirms the account holder's
  national id matches the nurse's. It is a seam: `Seams:BankOwnership:Provider` = `mock` (default) or
  `finnotech`. The mock returns a match for every IBAN **except** `Seams:BankOwnership:MismatchIban`
  (`IR000000000000000000000000`), which exists so the payout-gating path is testable.
- **Ownership verification gates payouts, and the gate lives in the payout engine, not here.**
  `EligibleNurseEarningsDto.hasVerifiedPrimaryIban` is the flag the admin console reads before generating
  a batch — see [payouts.md](payouts.md). A nurse with earnings and no verified primary IBAN accrues a
  balance and is simply not paid.
- Client-side IBAN handling (`iban.ts`) does checksum validation and formatting only. It is a UX
  affordance; the server re-validates.

## Enums

| Vocabulary | Values |
| --- | --- |
| `BankAccountStatus` | `pending` `verified` `mismatch` |

`mismatch` is a terminal, actionable state — the holder's national id did not match — and is distinct from
`pending`, which only means the inquiry has not run.

## Open REQs

None.
