> ## Documentation Index
> Fetch the complete documentation index at: https://docs.verifow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# KYC Verification

> How KYC status affects transaction screening and risk scoring

Every transaction checks the sender's KYC verification status using their BVN. The result feeds directly into the screening pipeline's KYC Verification engine.

## Identity Verification Providers

### Embedded Providers (Default)

BVN, NIN, and vNIN verification calls are made through integrated providers. When you trigger `verify-bvn` or `verify-nin`, the platform calls the provider's API with the ID number and cross-references the returned name against the applicant's name (including swapped name order, which is common in Nigeria). Liveness checks use face-match and liveness detection APIs.

### Bring Your Own License (BYOL)

Tenants can switch to **BYOL mode** and plug in their own KYC provider via a JSON configuration. This is useful when your institution already has a direct contract with an identity verification provider.

**How it works:**

| Mode       | Provider                                    | Configuration                 |
| ---------- | ------------------------------------------- | ----------------------------- |
| `EMBEDDED` | Platform-managed (primary → fallback chain) | None required                 |
| `BYOL`     | Tenant-managed via `CustomProvider`         | JSON config stored per-tenant |

**BYOL Configuration JSON** (stored in `Tenant.kycProviderConfig`):

```json theme={null}
{
  "providerName": "SmileIdentity",
  "baseUrl": "https://api.smileidentity.com/v1",
  "headers": { "Authorization": "Bearer sk_xxx" },
  "endpoints": {
    "nin": "/id_verification",
    "bvn": "/id_verification",
    "liveness": "/biometric"
  },
  "methods": { "nin": "POST" },
  "requestMapping": {
    "nin": { "id_number": "nin", "id_type": "'NIN'" }
  },
  "responseMapping": {
    "firstNamePath": "result.first_name",
    "lastNamePath": "result.last_name"
  },
  "supportedVerifications": ["nin", "bvn", "liveness"],
  "maxTier": "TIER_3",
  "matchConfidence": 92
}
```

**Key mapping fields:**

| Field                    | Purpose                                                                                                                                                                      |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requestMapping`         | Maps internal field names to provider API fields. Use `{{nin}}` for dynamic values or `'NIN'` for literals.                                                                  |
| `responseMapping`        | JSON paths to extract `firstName` and `lastName` from the provider response.                                                                                                 |
| `matchConfidence`        | Minimum confidence (0–100) to consider a name match successful.                                                                                                              |
| `supportedVerifications` | Which checks the BYOL provider supports (`nin`, `bvn`, `vnin`, `liveness`).                                                                                                  |
| `maxTier`                | Highest tier this BYOL provider can achieve (`TIER_1`, `TIER_2`, `TIER_3`). If target tier exceeds this, platform falls back to embedded providers (if fallback is enabled). |

Admins can switch modes and edit the BYOL config from **Dashboard → Settings → KYC Provider**.

## KYC Tiers

The platform implements CBN-mandated KYC tiers. Each tier maps to different verification requirements and transaction limits:

| Tier                | Requirements                                     | Approval From Status            | CBN Typical Limit    |
| ------------------- | ------------------------------------------------ | ------------------------------- | -------------------- |
| `TIER_1` · Basic    | BVN **or** NIN                                   | `NIN_VERIFIED` / `BVN_VERIFIED` | ₦20,000 single txn   |
| `TIER_2` · Standard | BVN + valid ID                                   | `BVN_VERIFIED`                  | ₦500,000 max balance |
| `TIER_3` · Premium  | BVN/NIN + valid ID + proof of address + liveness | `LIVENESS_PASSED`               | Unlimited            |

* Applications default to `TIER_1`, or `TIER_2` if both BVN and NIN are provided at creation
* Tier can be explicitly set at creation via the `tier` field
* **Auto-upgrade**: As applicants upload documents or complete verifications, the system automatically upgrades their tier when requirements are met
  * `TIER_1` → `TIER_2`: requires **verified BVN** + valid ID (NIN alone does not qualify)
  * `TIER_2` → `TIER_3`: requires verified BVN/NIN + valid ID + proof of address + liveness
* Approved applications can be manually upgraded to a higher tier via the dashboard
* Tier flows into transaction screening for rule-based limit enforcement

### Adding Identity Information After Creation

Applicants often don't have their BVN or NIN available when they first start the onboarding flow. The system supports updating these fields on an existing `PENDING` application via `PATCH /api/v1/kyc/applications/:id`:

```bash theme={null}
# Applicant comes back with their BVN
PATCH /api/v1/kyc/applications/kyc_abc123
{
  "bvn": "22012345678",
  "dateOfBirth": "1990-03-15"
}

# Then verify it
POST /api/v1/kyc/applications/kyc_abc123/verify-bvn
{
  "bvn": "22012345678",
  "firstName": "Chinedu",
  "lastName": "Obi",
  "dateOfBirth": "1990-03-15"
}
```

**Key rules:**

* Updates only allowed while status is `PENDING`, `DOCUMENT_UPLOADED`, `NIN_VERIFIED`, or `BVN_VERIFIED`
* NIN and BVN are validated as exactly 11 digits; empty strings clear the field
* If both BVN and NIN become present, tier auto-bumps from `TIER_1` to `TIER_2`
* After updating, the system re-evaluates `auto-upgrade` in case documents/verifications already qualify for a higher tier
* The verification endpoints (`verify-nin`, `verify-bvn`) accept the ID number in the request body, so the application record doesn't need to be pre-populated
* **Status preservation**: Verifying NIN when status is already `BVN_VERIFIED` or `LIVENESS_PASSED` does not downgrade the status. The highest milestone is always preserved.

## KYC Application Statuses

Applications progress through the following statuses during verification:

| Status              | Meaning                                        |
| ------------------- | ---------------------------------------------- |
| `PENDING`           | Submitted, awaiting document upload            |
| `DOCUMENT_UPLOADED` | ID documents uploaded                          |
| `NIN_VERIFIED`      | NIN check passed against provider              |
| `BVN_VERIFIED`      | BVN check passed against provider              |
| `LIVENESS_PASSED`   | Biometric liveness and face-match passed       |
| `APPROVED`          | Fully verified by compliance officer           |
| `REJECTED`          | Failed verification or fraudulent documents    |
| `EXPIRED`           | Verification expired, re-verification required |

## Screening Impact

When a transaction is screened, the KYC Verification engine evaluates the sender's status. If no KYC application exists, the engine flags it accordingly.

| Condition                         | Score | Outcome   | What Happens                                                             |
| --------------------------------- | ----- | --------- | ------------------------------------------------------------------------ |
| **No KYC record**                 | 100   | `BLOCK`   | Transaction blocked, officer notified, customer prompted to complete KYC |
| **PENDING**                       | 100   | `BLOCK`   | Transaction blocked unti-verification completes                          |
| **DOCUMENT\_UPLOADED**            | 100   | `BLOCK`   | Transaction blocked-ntil verification completes                          |
| **NIN\_VERIFIED / BVN\_VERIFIED** | 100   | `BLOCK`   | Maps to `IN_PROGRESS` — transaction blocked until fully verified         |
| **LIVENESS\_PASSED**              | 0     | `APPROVE` | Maps to `VERIFIED` — no risk contribution (Tier 3 equivalent)            |
| **APPROVED**                      | 0     | `APPROVE` | Fully verified — no risk contribution                                    |
| **EXPIRED**                       | 100   | `BLOCK`   | Transaction blocked, re-verification required                            |
| **REJECTED**                      | 100   | `BLOCK`   | Transaction blocked, enhanced due diligence required                     |

## KYC Trust Modes

Tenants can configure how KYC status is resolved during transaction screening:

| Mode       | Behavior                                                                                      |
| ---------- | --------------------------------------------------------------------------------------------- |
| `STRICT`   | **Default.** KYC records must exist in the DB. Payload KYC is ignored.                        |
| `HYBRID`   | Uses payload KYC if provided, falls back to DB lookup. `senderKycTier` optional.              |
| `EXTERNAL` | Requires KYC status **and** `senderKycTier` in every transaction payload. No local DB lookup. |

Set the mode via `PATCH /api/v1/tenants/me` with `kycTrustMode`.

## Document Upload & Storage

KYC applications support document uploads via the API. Documents are stored in **MinIO** (an S3-compatible object storage server) and referenced in the database.

### Supported Document Types

| Type              | Description                    |
| ----------------- | ------------------------------ |
| `PASSPORT`        | International passport         |
| `NATIONAL_ID`     | National identity card         |
| `DRIVERS_LICENSE` | Driver's license               |
| `VOTERS_CARD`     | Voter's card                   |
| `UTILITY_BILL`    | Utility bill (address proof)   |
| `BANK_STATEMENT`  | Bank statement                 |
| `SELFIE`          | Selfie photograph              |
| `LIVENESS_VIDEO`  | Liveness check video recording |
| `SIGNATURE`       | Signature specimen             |
| `OTHER`           | Any other document             |

### Document Endpoints

| Method   | Endpoint                                                 | Description                           |
| -------- | -------------------------------------------------------- | ------------------------------------- |
| `POST`   | `/api/v1/kyc/applications/:id/documents`                 | Upload document (multipart/form-data) |
| `GET`    | `/api/v1/kyc/applications/:id/documents`                 | List documents with presigned URLs    |
| `GET`    | `/api/v1/kyc/applications/:id/documents/:docId/download` | Get 5-minute presigned download URL   |
| `DELETE` | `/api/v1/kyc/applications/:id/documents/:docId`          | Delete document (admin only)          |

### How It Works

1. **Upload:** `POST` with `multipart/form-data` containing `file` and `documentType`
2. **Storage:** File is saved to MinIO under `kyc/{applicationId}/{timestamp}-{filename}`
3. **Database:** A `KYCDocument` record is created with the MinIO object key
4. **Retrieval:** The API returns **presigned URLs** (5-minute expiry) for secure viewing
5. **Deletion:** Admin users can delete documents — this removes from both MinIO and the database

### Upload Example

```bash theme={null}
curl -X POST /api/v1/kyc/applications/kyc_abc123/documents \
  -H "Authorization: Bearer <token>" \
  -F "file=@passport.pdf" \
  -F "documentType=PASSPORT"
```

> **Note:** Documents are tenant-scoped. You can only access documents belonging to applications in your tenant.

***

## BYOL Fallback Mode

When a BYOL provider is unavailable, tenants can configure the fallback behavior:

| Mode             | Behavior                                                               |
| ---------------- | ---------------------------------------------------------------------- |
| `NONE`           | **Default.** Returns `ServiceUnavailableException`.                    |
| `WALLET_RESERVE` | Falls back to the embedded provider if the wallet has available funds. |

Set the mode via `PATCH /api/v1/tenants/me` with `byolFallbackMode`.

> **Note**: If `senderBvn` is not provided in the request and no KYC payload is supplied, KYC status defaults to "No KYC record" (score 100, `BLOCK`). Always include the sender's BVN or KYC payload for accurate screening.
