# Domain 4 — Verification & Credentials

← Database Model

Related: business requirements — Nurse verification.

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`.

↑ Back to top