# Domain 4 — Verification & Credentials

[← Database Model](index.md)

**Related:** business requirements — [Nurse verification](../business/02-nurse-verification.md).

The pipeline stays **data-driven**: step types are rows, so a new regulatory requirement (e.g. professional liability insurance) is one INSERT. This revision adds a **structured credential registry** because the brand *is* "verified trust" and renewal tracking needs queryable license numbers, not opaque PDFs.

### `nurse_verifications` [CORE]
**Role:** The master per-nurse verification record; aggregates step outcomes into one status (the single source of truth for verification state). **Why a header table:** one place to drive the overall lifecycle and the `is_verified` flip. Fields unchanged: `id`, `nurse_id` (unique), `status` (`not_started`/`pending`/`in_review`/`approved`/`rejected`/`suspended`), `submitted_at`, `approved_at`, `rejected_at`, `suspended_at`, `rejection_reason`, `reviewed_by_admin_id`, `internal_notes`, timestamps. **Relations:** 1:1 → `nurse_profiles`; 1:N → `verification_steps`.

### `verification_step_types` [CORE]
**Role:** Admin catalog of pipeline steps with stable machine `code`s (`identity_kyc`, `shahkar_match`, `moh_competency_license`, `ino_membership`, `criminal_record`, `bank_account_verification`). **Why rows + `is_automated`/`automation_provider`:** the Iranian credential reality is fragmented across regulators and partly automatable (Shahkar, liveness) and partly manual (license PDF) — data-driving it absorbs that without code changes. Fields unchanged. **Relations:** 1:N → `verification_steps`.

### `verification_steps` [CORE]
**Role:** One row per step per nurse; tracks status, the raw `external_response_json` (KYC vendor audit), and `expires_at` for time-limited steps (the عدم سوء پیشینه certificate expires → step reverts to `pending` + raises a `support_alert`). **Why snapshot `is_automated`:** historical records survive later step-type edits. Fields unchanged, with `UNIQUE(nurse_verification_id, step_type_id)`. **Relations:** N:1 → `nurse_verifications`, `verification_step_types`; 1:N → `verification_documents`.

### `verification_documents` [CORE]
**Role:** Uploaded evidence metadata (object-storage key + integrity hash); files live in S3-compatible storage behind signed URLs, never public. **Why metadata-only:** keeps PII bytes out of the DB and access controlled. Fields unchanged. **Relations:** N:1 → `verification_steps`.

### `nurse_credentials` [MVP] — **NEW**
**Role:** Structured, queryable registry of the actual Iranian credentials — beyond the opaque document uploads. **Why:** no public B2B API exists for MoH/INO, so an admin manually verifies an uploaded credential against the official portal — but the old model gave them **nowhere to record the verified license number** for renewal alerts, the public trust badge, or cross-check. This makes the badge and expiry monitoring real and survives a future INO/MoH API.

| Field | Type | Notes |
|---|---|---|
| `id` | BIGINT PK | |
| `nurse_id` | BIGINT FK → nurse_profiles | |
| `credential_type` | NVARCHAR(50) | `moh_competency_license` (پروانه صلاحیت حرفه‌ای) / `ino_membership` (نظام پرستاری) / `criminal_record` (عدم سوء پیشینه) |
| `credential_number` | NVARCHAR(100) (enc) | License/membership number |
| `holder_name_snapshot` | NVARCHAR(200) | Name as printed, for ID cross-check |
| `issuing_authority` | NVARCHAR(200) | |
| `issued_at`, `expires_at` | DATE NULL | Drives renewal alerts |
| `verification_source` | NVARCHAR(300) NULL | Portal URL / method |
| `verification_method` | NVARCHAR(20) | `manual` / `portal` / `api` |
| `verified_by_admin_id` | BIGINT FK → users NULL | |
| `created_at`, `updated_at` | … | |

**Relations:** N:1 → `nurse_profiles`. Cross-referenced by the relevant `verification_steps`.
