> ## 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

> Manage Know Your Customer and Know Your Business verification applications

## Overview

The KYC module handles identity verification for your customers. Applications flow through statuses from submission to final approval, with BVN and NIN verification steps against national identity providers.

## Common Workflows

**New customer onboarding:** Customer submits KYC application → System verifies BVN/NIN against provider APIs → Officer reviews and approves → Customer can transact without KYC-related risk flags.

**Re-verification:** A customer's KYC expires → They submit updated documents → Officer verifies and approves → KYC status updates to `APPROVED`.

**Business onboarding (KYB):** Officer creates KYB application → CAC lookup fetches company details → Directors verified via NIN/BVN → Beneficial owners screened against sanctions → Officer approves → Business can transact without KYB risk flags.

## Permissions

| Action                                         | Who Can Do It                      |
| ---------------------------------------------- | ---------------------------------- |
| Submit, list, view applications                | All authenticated users            |
| Verify BVN/NIN                                 | `BANK_ADMIN`, `COMPLIANCE_OFFICER` |
| Approve/reject KYC                             | `BANK_ADMIN`, `COMPLIANCE_OFFICER` |
| Create KYB application                         | `BANK_ADMIN`, `COMPLIANCE_OFFICER` |
| Run CAC lookup / verify directors / screen BOs | `BANK_ADMIN`, `COMPLIANCE_OFFICER` |
| Approve/reject KYB                             | `BANK_ADMIN`, `COMPLIANCE_OFFICER` |

## KYC Statuses

| Status              | Meaning                             | Risk Impact                                |
| ------------------- | ----------------------------------- | ------------------------------------------ |
| `PENDING`           | Submitted, awaiting document upload | Score 100, `BLOCK`                         |
| `DOCUMENT_UPLOADED` | ID documents uploaded               | Score 100, `BLOCK`                         |
| `NIN_VERIFIED`      | NIN check passed                    | Score 100, `BLOCK` (maps to `IN_PROGRESS`) |
| `BVN_VERIFIED`      | BVN check passed                    | Score 100, `BLOCK` (maps to `IN_PROGRESS`) |
| `LIVENESS_PASSED`   | Biometric liveness check passed     | Score 0, `APPROVE` (maps to `VERIFIED`)    |
| `APPROVED`          | Fully verified                      | Score 0, `APPROVE`                         |
| `REJECTED`          | Failed verification                 | Score 100, `BLOCK`                         |
| `EXPIRED`           | Verification expired                | Score 100, `BLOCK`                         |

## KYC Tiers (CBN-Compliant)

The platform supports CBN-mandated account tiers. Each tier requires a different level of identity verification:

| Tier                | Requirements                                                | Typical Limits (CBN) |
| ------------------- | ----------------------------------------------------------- | -------------------- |
| `TIER_1` · Basic    | BVN **or** NIN                                              | ₦20,000 single txn   |
| `TIER_2` · Standard | BVN + valid ID (passport / driver's license / voter's card) | ₦500,000 max balance |
| `TIER_3` · Premium  | BVN/NIN + valid ID + proof of address + liveness check      | Unlimited            |

**Key behaviors:**

* Applications default to `TIER_1` if no tier is specified at creation
* Tier 1 requires **either** BVN or NIN verification
* Tier 2 requires a **linked BVN** plus a **valid means of identification** (Passport, Driver's License, Voter's Card, or National ID)
* Tier 3 adds **proof of address** and a **liveness/biometric check**
* **Smart tier default**: If both BVN and NIN are provided at creation, the application defaults to `TIER_2`
* **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 is insufficient for Tier 2)
  * `TIER_2` → `TIER_3`: requires verified BVN/NIN + valid ID + proof of address + liveness
* The admin **reviews** documents uploaded by the applicant — never uploads on their behalf
* The `tier` field is stored on the `KYCApplication` and flows into transaction screening for rule-based limit enforcement

### Applicant "Comes Back" — Adding BVN/NIN Later

Not all applicants provide both BVN and NIN at creation. The system supports a **resume workflow** where an applicant (or officer on their behalf) can add missing identity numbers to an existing `PENDING` application.

**Scenario:**

1. Applicant creates application with only name and phone → Status: `PENDING`, Tier: `TIER_1`
2. Later, applicant provides their NIN via mobile app or branch visit
3. Officer uses `PATCH /api/v1/kyc/applications/:id` to add the NIN
4. Officer clicks **"Verify NIN"** → Status becomes `NIN_VERIFIED`
5. Applicant is now Tier 1 approved (or can proceed to Tier 2/3)

```bash theme={null}
# Officer adds missing NIN to an existing application
PATCH /api/v1/kyc/applications/kyc_abc123
{
  "nin": "12345678901"
}

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

**Editable fields:** `nin`, `bvn`, `email`, `phone`, `dateOfBirth`
**Allowed statuses:** `PENDING`, `DOCUMENT_UPLOADED`, `NIN_VERIFIED`, `BVN_VERIFIED`
**Validation:** NIN and BVN must be exactly 11 digits; empty strings clear the field; invalid dates are rejected
**Smart tier bump:** If both BVN and NIN become present, tier auto-bumps to `TIER_2`. If documents/verifications already exist, `auto-upgrade` is also evaluated.

### Status Preservation During Verification

The KYC `status` field tracks the **highest verified milestone** reached. Verifying an identity type never downgrades a higher status:

| Current Status    | Verify NIN                  | Verify BVN                  | Result                      |
| ----------------- | --------------------------- | --------------------------- | --------------------------- |
| `PENDING`         | `NIN_VERIFIED`              | `BVN_VERIFIED`              | Normal progression          |
| `NIN_VERIFIED`    | `NIN_VERIFIED`              | `BVN_VERIFIED`              | Upgrades to BVN             |
| `BVN_VERIFIED`    | **stays `BVN_VERIFIED`**    | `BVN_VERIFIED`              | NIN does not downgrade      |
| `LIVENESS_PASSED` | **stays `LIVENESS_PASSED`** | **stays `LIVENESS_PASSED`** | Preserves highest milestone |

This ensures a Tier 2 application at `BVN_VERIFIED` remains approvable even if NIN is later verified, and a Tier 3 application at `LIVENESS_PASSED` remains approvable if additional identity checks are run.

### Tier Upgrade Flow

1. Applicant is approved at **Tier 1** (NIN verified only)
2. Later, they provide BVN → Admin clicks **"Upgrade to Tier 2"**
3. System resets status to `NIN_VERIFIED` (preserving the verified NIN)
4. Admin verifies the BVN → Status becomes `BVN_VERIFIED`
5. Admin approves → Application is now **Tier 2 · Approved**

### BYOL Provider Tier Limits

If a tenant uses BYOL (Bring Your Own License), their provider may not support all verification types needed for higher tiers. Configure `maxTier` and `supportedVerifications` in the BYOL config:

```json theme={null}
{
  "providerName": "SmileIdentity",
  "maxTier": "TIER_1",
  "supportedVerifications": ["nin", "bvn"]
  // ... rest of config
}
```

If a tenant requests Tier 2/3 but their BYOL provider's `maxTier` is lower:

* With `kycFallbackMode = WALLET_RESERVE`: Platform automatically falls back to embedded providers for the missing verification types
* With `kycFallbackMode = NONE`: The verification is blocked with a clear error message

## KYC Endpoints

| Method   | Endpoint                                                 | Description                  |
| -------- | -------------------------------------------------------- | ---------------------------- |
| `POST`   | `/api/v1/kyc/applications`                               | Submit a KYC application     |
| `GET`    | `/api/v1/kyc/applications`                               | List applications            |
| `GET`    | `/api/v1/kyc/applications/:id`                           | Retrieve application details |
| `PATCH`  | `/api/v1/kyc/applications/:id`                           | Update identity fields       |
| `POST`   | `/api/v1/kyc/applications/:id/verify-bvn`                | Verify applicant BVN         |
| `POST`   | `/api/v1/kyc/applications/:id/verify-nin`                | Verify applicant NIN         |
| `POST`   | `/api/v1/kyc/applications/:id/liveness-check`            | Run biometric liveness check |
| `POST`   | `/api/v1/kyc/applications/:id/documents`                 | Upload document to MinIO     |
| `GET`    | `/api/v1/kyc/applications/:id/documents`                 | List uploaded documents      |
| `GET`    | `/api/v1/kyc/applications/:id/documents/:docId/download` | Get presigned download URL   |
| `DELETE` | `/api/v1/kyc/applications/:id/documents/:docId`          | Delete document from MinIO   |
| `PATCH`  | `/api/v1/kyc/applications/:id/approve`                   | Approve application          |
| `PATCH`  | `/api/v1/kyc/applications/:id/reject`                    | Reject application           |
| `POST`   | `/api/v1/kyc/applications/:id/upgrade-tier`              | Upgrade to Tier 2 or 3       |
| `GET`    | `/api/v1/kyc/applications/:id/tier-requirements`         | Check tier requirements      |

## KYB Statuses

| Status                       | Meaning                                    | Risk Impact        |
| ---------------------------- | ------------------------------------------ | ------------------ |
| `PENDING`                    | Application submitted, awaiting CAC lookup | Score 100, `BLOCK` |
| `CAC_VERIFIED`               | CAC lookup successful                      | Score 100, `BLOCK` |
| `DIRECTORS_VERIFIED`         | Directors identity verified                | Score 100, `BLOCK` |
| `BENEFICIAL_OWNERS_VERIFIED` | Beneficial owners screened                 | Score 100, `BLOCK` |
| `DOCUMENT_UPLOADED`          | Supporting documents uploaded              | Score 100, `BLOCK` |
| `SCREENING_PASSED`           | All compliance checks passed               | Score 100, `BLOCK` |
| `APPROVED`                   | Fully verified                             | Score 0, `APPROVE` |
| `REJECTED`                   | Failed verification or sanctions hit       | Score 100, `BLOCK` |

> **Note:** KYB is a hard prerequisite for business transactions. Only `APPROVED` status allows transactions to proceed without risk flags. All intermediate statuses score 100 and result in `BLOCK`.

## Per-Tenant KYC Provider Configuration

Each tenant can independently configure how identity verification is performed.

### Provider Modes

| Mode       | Description                                                   |
| ---------- | ------------------------------------------------------------- |
| `EMBEDDED` | Uses platform-managed providers. Default for all new tenants. |
| `BYOL`     | Uses the tenant's own KYC provider API, configured via JSON.  |

### Switching Provider Mode

`BANK_ADMIN` users can switch modes from **Dashboard → Settings → KYC Provider**:

1. Select **Embedded** or **BYOL** from the dropdown.
2. If BYOL, fill in the configuration form:
   * Provider Name
   * Base URL
   * API Key / App ID
   * Endpoint paths for NIN, BVN, and Liveness
   * Request/response field mappings
   * Match confidence threshold
3. Save -the change takes effect immediately for all new verifications.

### BYOL Configuration Format

The configuration is stored as JSON 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", "bvn": "POST" },
  "requestMapping": {
    "nin": { "id_number": "nin", "id_type": "'NIN'" },
    "bvn": { "id_number": "bvn", "id_type": "'BVN'" }
  },
  "responseMapping": {
    "firstNamePath": "result.first_name",
    "lastNamePath": "result.last_name"
  },
  "matchConfidence": 92
}
```

## KYC Endpoints

| Method   | Endpoint                                                 | Description                  |
| -------- | -------------------------------------------------------- | ---------------------------- |
| `POST`   | `/api/v1/kyc/applications`                               | Submit a KYC application     |
| `GET`    | `/api/v1/kyc/applications`                               | List applications            |
| `GET`    | `/api/v1/kyc/applications/:id`                           | Retrieve application details |
| `POST`   | `/api/v1/kyc/applications/:id/verify-bvn`                | Verify applicant BVN         |
| `POST`   | `/api/v1/kyc/applications/:id/verify-nin`                | Verify applicant NIN         |
| `POST`   | `/api/v1/kyc/applications/:id/liveness-check`            | Run biometric liveness check |
| `POST`   | `/api/v1/kyc/applications/:id/documents`                 | Upload document to MinIO     |
| `GET`    | `/api/v1/kyc/applications/:id/documents`                 | List uploaded documents      |
| `GET`    | `/api/v1/kyc/applications/:id/documents/:docId/download` | Get presigned download URL   |
| `DELETE` | `/api/v1/kyc/applications/:id/documents/:docId`          | Delete document from MinIO   |
| `PATCH`  | `/api/v1/kyc/applications/:id/approve`                   | Approve application          |
| `PATCH`  | `/api/v1/kyc/applications/:id/reject`                    | Reject application           |

## KYB Endpoints

| Method   | Endpoint                                                     | Description                    |
| -------- | ------------------------------------------------------------ | ------------------------------ |
| `POST`   | `/api/v1/kyc/kyb/applications`                               | Create a KYB application       |
| `GET`    | `/api/v1/kyc/kyb/applications`                               | List KYB applications          |
| `GET`    | `/api/v1/kyc/kyb/applications/:id`                           | Get KYB application details    |
| `POST`   | `/api/v1/kyc/kyb/applications/:id/verify-cac`                | Run CAC lookup by RC number    |
| `POST`   | `/api/v1/kyc/kyb/applications/:id/verify-directors`          | Verify all directors via KYC   |
| `POST`   | `/api/v1/kyc/kyb/applications/:id/screen-beneficial-owners`  | Screen BOs against sanctions   |
| `POST`   | `/api/v1/kyc/kyb/applications/:id/documents`                 | Upload KYB document to MinIO   |
| `GET`    | `/api/v1/kyc/kyb/applications/:id/documents`                 | List KYB uploaded documents    |
| `GET`    | `/api/v1/kyc/kyb/applications/:id/documents/:docId/download` | Get presigned download URL     |
| `DELETE` | `/api/v1/kyc/kyb/applications/:id/documents/:docId`          | Delete KYB document from MinIO |
| `PATCH`  | `/api/v1/kyc/kyb/applications/:id/approve`                   | Approve KYB application        |
| `PATCH`  | `/api/v1/kyc/kyb/applications/:id/reject`                    | Reject KYB application         |

## Tenant Configuration Endpoints

| Method  | Endpoint             | Description                                                                                                   |
| ------- | -------------------- | ------------------------------------------------------------------------------------------------------------- |
| `PATCH` | `/api/v1/tenants/me` | Update tenant config including `kycProviderMode`, `kycProviderConfig`, `kycTrustMode`, and `byolFallbackMode` |

***

### Submit KYC Application

Create a new KYC application for a customer.

**Request Body**

| Field         | Type     | Required | Description                                           |
| ------------- | -------- | -------- | ----------------------------------------------------- |
| `entityType`  | `string` | ✅        | `INDIVIDUAL` or `BUSINESS`                            |
| `firstName`   | `string` | ✅        | Applicant first name                                  |
| `lastName`    | `string` | ✅        | Applicant last name                                   |
| `bvn`         | `string` |          | Bank Verification Number                              |
| `nin`         | `string` |          | National Identity Number                              |
| `phone`       | `string` |          | Mobile number with country code                       |
| `email`       | `string` |          | Email address                                         |
| `dateOfBirth` | `string` |          | ISO 8601 date (e.g. `"1990-03-15"`)                   |
| `tier`        | `string` |          | `TIER_1`, `TIER_2`, or `TIER_3`. Defaults to `TIER_1` |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/applications \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "entityType": "INDIVIDUAL",
    "firstName": "Chinedu",
    "lastName": "Obi",
    "bvn": "22012345678",
    "nin": "12345678901",
    "phone": "+2348012345678",
    "email": "chinedu.obi@email.com",
    "dateOfBirth": "1990-03-15"
  }'
```

**Example Response -201 Created**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyc_abc123",
    "tenantId": "660e8400-e29b-41d4-a716-446655440001",
    "entityType": "INDIVIDUAL",
    "firstName": "Chinedu",
    "lastName": "Obi",
    "bvn": "22012345678",
    "nin": "12345678901",
    "phone": "+2348012345678",
    "email": "chinedu.obi@email.com",
    "dateOfBirth": "1990-03-15T00:00:00Z",
    "status": "PENDING",
    "riskLevel": null,
    "assignedTo": null,
    "notes": null,
    "createdAt": "2026-05-16T08:00:00Z",
    "updatedAt": "2026-05-16T08:00:00Z"
  }
}
```

***

### Create KYB Application

Create a new KYB application for a corporate entity.

**Request Body**

| Field                           | Type     | Required | Description                       |
| ------------------------------- | -------- | -------- | --------------------------------- |
| `companyName`                   | `string` | ✅        | Registered company name           |
| `rcNumber`                      | `string` |          | CAC registration number           |
| `tin`                           | `string` |          | Tax Identification Number         |
| `address`                       | `string` |          | Registered business address       |
| `incorporationDate`             | `string` |          | ISO 8601 date                     |
| `directors`                     | `array`  |          | Array of director objects         |
| `directors[].name`              | `string` | ✅        | Director full name                |
| `directors[].bvn`               | `string` |          | Director BVN                      |
| `directors[].nin`               | `string` |          | Director NIN                      |
| `directors[].email`             | `string` |          | Director email                    |
| `directors[].phone`             | `string` |          | Director phone                    |
| `directors[].nationality`       | `string` |          | Director nationality              |
| `beneficialOwners`              | `array`  |          | Array of beneficial owner objects |
| `beneficialOwners[].name`       | `string` | ✅        | Owner full name                   |
| `beneficialOwners[].percentage` | `number` | ✅        | Ownership percentage              |
| `beneficialOwners[].bvn`        | `string` |          | Owner BVN                         |
| `beneficialOwners[].nin`        | `string` |          | Owner NIN                         |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/kyb/applications \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "companyName": "Acme Nigeria Ltd",
    "rcNumber": "RC123456",
    "tin": "12345678-0001",
    "address": "15 Broad Street, Lagos",
    "incorporationDate": "2015-06-12",
    "directors": [
      { "name": "John Doe", "bvn": "22012345678", "email": "john@acme.ng" }
    ],
    "beneficialOwners": [
      { "name": "Jane Smith", "percentage": 75, "bvn": "22087654321" }
    ]
  }'
```

***

### Verify CAC

Run a CAC lookup for a KYB application.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYB Application UUID |

**Request Body**

| Field      | Type     | Required | Description                        |
| ---------- | -------- | -------- | ---------------------------------- |
| `rcNumber` | `string` | ✅        | CAC registration number to look up |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/kyb/applications/kyb_abc123/verify-cac \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "rcNumber": "RC123456"
  }'
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyb_abc123",
    "status": "CAC_VERIFIED",
    "companyName": "Acme Nigeria Ltd",
    "rcNumber": "RC123456",
    "directors": [{ "name": "John Doe", "role": "Director" }],
    "beneficialOwners": [{ "name": "Jane Smith", "percentage": 75 }],
    "updatedAt": "2026-05-16T09:00:00Z"
  }
}
```

***

### List KYC Applications

**Query Parameters**

| Parameter | Type      | Default | Description      |
| --------- | --------- | ------- | ---------------- |
| `status`  | `string`  | -       | Filter by status |
| `page`    | `integer` | `1`     | Page number      |
| `limit`   | `integer` | `20`    | Items per page   |

**Example Request**

```bash theme={null}
curl -X GET "/v1/kyc/applications?status=PENDING&page=1&limit=10" \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "kyc_abc123",
        "entityType": "INDIVIDUAL",
        "firstName": "Chinedu",
        "lastName": "Obi",
        "status": "PENDING",
        "riskLevel": null,
        "createdAt": "2026-05-16T08:00:00Z",
        "updatedAt": "2026-05-16T08:00:00Z"
      }
    ],
    "total": 5,
    "page": 1,
    "limit": 10,
    "totalPages": 1
  }
}
```

***

### Get KYC Application

Retrieve a KYC application with full verification results and uploaded documents.

**Example Request**

```bash theme={null}
curl -X GET /v1/kyc/applications/kyc_abc123 \
  -H "Authorization: Bearer <access_token>"
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyc_abc123",
    "tenantId": "660e8400-e29b-41d4-a716-446655440001",
    "entityType": "INDIVIDUAL",
    "status": "BVN_VERIFIED",
    "firstName": "Chinedu",
    "lastName": "Obi",
    "bvn": "22012345678",
    "nin": "12345678901",
    "phone": "+2348012345678",
    "email": "chinedu.obi@email.com",
    "dateOfBirth": "1990-03-15T00:00:00Z",
    "riskLevel": null,
    "assignedTo": null,
    "notes": null,
    "createdAt": "2026-05-16T08:00:00Z",
    "updatedAt": "2026-05-16T09:00:00Z",
    "verificationResults": [
      {
        "id": "vr_001",
        "applicationId": "kyc_abc123",
        "identityType": "BVN",
        "provider": "PREMBLY",
        "isMatch": true,
        "confidence": 0.95,
        "rawResponse": null,
        "errorMessage": null,
        "verifiedAt": "2026-05-16T09:00:00Z"
      }
    ],
    "documents": [
      {
        "id": "doc_001",
        "applicationId": "kyc_abc123",
        "documentType": "PASSPORT",
        "fileName": "passport.pdf",
        "fileUrl": "kyc-documents/passport-123.pdf",
        "fileSizeBytes": 204800,
        "mimeType": "application/pdf",
        "uploadedAt": "2026-05-16T08:30:00Z"
      }
    ]
  }
}
```

***

### Verify BVN

Trigger BVN verification against the identity provider.

**Path Parameters**

| Parameter | Type     | Description      |
| --------- | -------- | ---------------- |
| `id`      | `string` | Application UUID |

**Request Body**

| Field         | Type     | Required | Description                   |
| ------------- | -------- | -------- | ----------------------------- |
| `bvn`         | `string` | ✅        | BVN to verify (11 digits)     |
| `firstName`   | `string` | ✅        | First name for cross-match    |
| `lastName`    | `string` | ✅        | Last name for cross-match     |
| `dateOfBirth` | `string` | ✅        | Date of birth for cross-match |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/applications/kyc_abc123/verify-bvn \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "bvn": "22012345678",
    "firstName": "Chinedu",
    "lastName": "Obi",
    "dateOfBirth": "1990-03-15"
  }'
```

**Example Response -200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "isMatch": true,
    "confidence": 0.95,
    "provider": "PREMBLY",
    "providerSource": "EMBEDDED",
    "newStatus": "BVN_VERIFIED"
  }
}
```

> **Auto-upgrade behavior:** If the applicant has already uploaded a valid ID document, successful BVN verification may auto-upgrade the application from Tier 1 to Tier 2.

***

### Verify NIN

Trigger NIN verification.

**Path Parameters**

| Parameter | Type     | Description      |
| --------- | -------- | ---------------- |
| `id`      | `string` | Application UUID |

**Request Body**

| Field         | Type     | Required | Description                   |
| ------------- | -------- | -------- | ----------------------------- |
| `nin`         | `string` | ✅        | NIN to verify (11 digits)     |
| `firstName`   | `string` | ✅        | First name for cross-match    |
| `lastName`    | `string` | ✅        | Last name for cross-match     |
| `dateOfBirth` | `string` | ✅        | Date of birth for cross-match |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/applications/kyc_abc123/verify-nin \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "nin": "12345678901",
    "firstName": "Chinedu",
    "lastName": "Obi",
    "dateOfBirth": "1990-03-15"
  }'
```

* **Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "isMatch": true,
    "confidence": 0.94,
    "provider": "PREMBLY",
    "providerSource": "EMBEDDED",
    "newStatus": "NIN_VERIFIED"
  }
}
```

***

### Liveness Check

Run a biometric liveness and face-match check.

**Path Parameters**

| Parameter | Type     | Description      |
| --------- | -------- | ---------------- |
| `id`      | `string` | Application UUID |

**Request Body**

| Field                 | Type     | Required | Description                               |
| --------------------- | -------- | -------- | ----------------------------------------- |
| `selfieImageBase64`   | `string` | ✅        | Base64-encoded selfie image (JPEG/PNG)    |
| `documentImageBase64` | `string` |          | Base64-encoded ID document for face-match |

**Example Request**

````bash theme={null}
curl -X POST /v1/kyc/applications/kyc_abc123/liveness-check \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "selfieImageBase64": "/9j/4AAQSkZJRgABAQEASABIAAD...",
    "documentImageBase64": "/9j/4AAQSkZJRgABAQEASABIAAD..."
  }'
```-

**Example Response — 200 OK**

```json
{
  "success": true,
  "data": {
    "isLive": true,
    "confidence": 0.97,
    "faceMatch": true,
    "faceMatchConfidence": 0.92,
    "provider": "PREMBLY",
    "providerSource": "EMBEDDED",
    "newStatus": "LIVENESS_PASSED"
  }
}
````

> **Auto-upgrade behavior:** If the applicant has already uploaded a valid ID and proof of address, successful liveness verification may auto-upgrade the application from Tier 2 to Tier 3.

***

### Approve Application

Final approval of a KYC application.

**Path Parameters**

| Parameter | Type     | Description      |
| --------- | -------- | ---------------- |
| `id`      | `string` | Application UUID |

**Example Request**

```bash theme={null}
curl -X PATCH /v1/kyc/applications/kyc_abc123/approve \
  -H "Authorization: Bearer <access_token>"
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyc_abc123",
    "status": "APPROVED",
    "riskLevel": "LOW",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
```

<Note>
  Approving a KYC application updates the customer's risk profile. Future
  transactions from this customer will reflect the `APPROVED` status.
</Note>

***

### Reject Application

Reject a KYC application that fails verification.

**Path Parameters**

| Parameter | Type     | Description      |
| --------- | -------- | ---------------- |
| `id`      | `string` | Application UUID |

**Request Body**

| Field    | Type     | Required | Description          |
| -------- | -------- | -------- | -------------------- |
| `reason` | `string` | ✅        | Reason for rejection |

**Example Request**

```bash theme={null}
curl -X PATCH /v1/kyc/applications/kyc_abc123/reject \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "BVN name mismatch. Uploaded document appears fraudulent."
  }'-
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyc_abc123",
    "status": "REJECTED",
    "riskLevel": "HIGH",
    "notes": "BVN name mismatch. Uploaded document appears fraudulent.",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
```

***

### Verify Directors

Verify each director's identity via BVN/NIN against identity providers.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYB Application UUID |

**Request Body**

| Field                  | Type      | Required | Description               |
| ---------------------- | --------- | -------- | ------------------------- |
| `directors`            | `array`   | ✅        | Array of director objects |
| `directors[].name`     | `string`  | ✅        | Director full name        |
| `directors[].nin`      | `string`  |          | Director NIN              |
| `directors[].bvn`      | `string`  |          | Director BVN              |
| `directors[].verified` | `boolean` |          | Pre-verified flag         |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/kyb/applications/kyb_abc123/verify-directors \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "directors": [
      { "name": "John Doe", "nin": "12345678901", "bvn": "22012345678", "verified": true }
    ]
  }'-
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "allVerified": true,
    "directors": [
      {
        "name": "John Doe",
        "nin": "12345678901",
        "bvn": "22012345678",
        "verified": true
      }
    ],
    "newStatus": "DIRECTORS_VERIFIED"
  }
}
```

***

### Screen Beneficial Owners

Screen beneficial owners against global sanctions and PEP watchlists.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYB Application UUID |

**Request Body**

| Field                      | Type      | Required | Description                 |
| -------------------------- | --------- | -------- | --------------------------- |
| `beneficialOwners`         | `array`   | ✅        | Array of BO objects         |
| `beneficialOwners[].name`  | `string`  | ✅        | Owner full name             |
| `beneficialOwners[].clean` | `boolean` | ✅        | Pre-screened clean flag     |
| `beneficialOwners[].hits`  | `array`   |          | Watchlist hits if not clean |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/kyb/applications/kyb_abc123/screen-beneficial-owners \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "beneficialOwners": [
      { "name": "Jane Smith", "clean": true, "hits": [] }
    ]
  }'-
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "allClean": true,
    "beneficialOwners": [{ "name": "Jane Smith", "clean": true, "hits": [] }],
    "newStatus": "BENEFICIAL_OWNERS_VERIFIED"
  }
}
```

***

### Approve KYB Application

Final approval of a KYB application. Requires `BANK_ADMIN` or `COMPLIANCE_OFFICER` role.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYB Application UUID |

**Example Request**

```bash theme={null}
curl -X PATCH /v1/kyc/kyb/applications/kyb_abc123/approve \
  -H "Authorization: Bearer <access_token>"
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyb_abc123",
    "status": "APPROVED",
    "riskLevel": "LOW",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
```

<Note>
  Approving a KYB application updates the business entity's risk profile. Future
  transactions from this entity will reflect the `APPROVED` status (score 0, no
  KYB-related risk).
</Note>

***

### Reject KYB Application

Reject a KYB application that fails verification. Requires `BANK_ADMIN` or `COMPLIANCE_OFFICER` role.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYB Application UUID |

**Request Body**

| Field    | Type     | Required | Description          |
| -------- | -------- | -------- | -------------------- |
| `reason` | `string` | ✅        | Reason for rejection |

**Example Request**

```bash theme={null}
curl -X PATCH /v1/kyc/kyb/applications/kyb_abc123/reject \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Sanctions hit on beneficial owner Jane Smith."
  }'-
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "kyb_abc123",
    "status": "REJECTED",
    "riskLevel": "HIGH",
    "notes": "Sanctions hit on beneficial owner Jane Smith.",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
```

***

## KYC Document Upload

Upload, manage, and retrieve KYC application documents stored in MinIO (S3-compatible object storage).

### Upload KYC Document

`POST /api/v1/kyc/applications/:id/documents`

Upload a file to MinIO and attach it to a KYC application.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYC Application UUID |

**Request Body (multipart/form-data)**

| Field          | Type     | Required | Description                                                                                                                                     |
| -------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `file`         | `file`   | ✅        | The file to upload (PDF, JPG, PNG, max 10MB)                                                                                                    |
| `documentType` | `string` | ✅        | `PASSPORT`, `NATIONAL_ID`, `DRIVERS_LICENSE`, `VOTERS_CARD`, `UTILITY_BILL`, `BANK_STATEMENT`, `SELFIE`, `LIVENESS_VIDEO`, `SIGNATURE`, `OTHER` |

**Example Request**

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

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "doc_001",
    "documentType": "PASSPORT",
    "fileName": "passport.pdf",
    "fileSizeBytes": 204800,
    "mimeType": "application/pdf",
    "uploadedAt": "2026-05-16T08:30:00Z",
    "presignedUrl": "documents/kyc/kyc_abc123/1715851200000-passport.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256..."
  }
}
```

> **Auto-upgrade behavior:** After a document is uploaded, the system automatically checks if the applicant now meets the requirements for a higher tier. For example, uploading a `PASSPORT` after BVN verification may auto-upgrade the application from Tier 1 to Tier 2.

***

### List KYC Documents

`GET /api/v1/kyc/applications/:id/documents`

List all documents for a KYC application with presigned URLs.

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "doc_001",
      "documentType": "PASSPORT",
      "fileName": "passport.pdf",
      "fileSizeBytes": 204800,
      "mimeType": "application/pdf",
      "uploadedAt": "2026-05-16T08:30:00Z",
      "presignedUrl": "documents/kyc/kyc_abc123/1715851200000-passport.pdf?X-Amz-Algorithm=..."
    }
  ]
}
```

***

### Get KYC Document Download URL

`GET /api/v1/kyc/applications/:id/documents/:docId/download`

Get a time-limited presigned URL to download a document directly from MinIO.

**Path Parameters**

| Parameter | Type     | Description          |
| --------- | -------- | -------------------- |
| `id`      | `string` | KYC Application UUID |
| `docId`   | `string` | Document UUID        |

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "url": "documents/kyc/kyc_abc123/1715851200000-passport.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256...",
    "expiresIn": 300
  }
}
```

***

### Delete KYC Document

`DELETE /api/v1/kyc/applications/:id/documents/:docId`

Delete a document from MinIO and the database. Requires `BANK_ADMIN` or `COMPLIANCE_OFFICER` role.

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": { "deleted": true }
}
```

***

## KYB Document Upload

Upload, manage, and retrieve KYB application documents stored in MinIO.

### Upload KYB Document

`POST /api/v1/kyc/kyb/applications/:id/documents`

**Request Body (multipart/form-data)**

| Field          | Type     | Required | Description                                                                                                                                         |
| -------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `file`         | `file`   | ✅        | The file to upload (PDF, JPG, PNG, max 10MB)                                                                                                        |
| `documentType` | `string` | ✅        | `CAC_CERTIFICATE`, `MEMART`, `TAX_CLEARANCE`, `BOARD_RESOLUTION`, `AUDITED_ACCOUNTS`, `UTILITY_BILL`, `DIRECTOR_ID`, `BENEFICIAL_OWNER_ID`, `OTHER` |

**Example Request**

```bash theme={null}
curl -X POST /v1/kyc/kyb/applications/kyb_abc123/documents \
  -H "Authorization: Bearer <access_token>" \
  -F "file=@/path/to/cac_certificate.pdf" \
  -F "documentType=CAC_CERTIFICATE"
```

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "doc_kyb_001",
    "documentType": "CAC_CERTIFICATE",
    "fileName": "cac_certificate.pdf",
    "fileSizeBytes": 512000,
    "mimeType": "application/pdf",
    "uploadedAt": "2026-05-16T09:00:00Z",
    "presignedUrl": "documents/kyb/kyb_abc123/1715853000000-cac_certificate.pdf?X-Amz-Algorithm=..."
  }
}
```

***

### List KYB Documents

`GET /api/v1/kyc/kyb/applications/:id/documents`

List all documents for a KYB application with presigned URLs.

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "doc_kyb_001",
      "documentType": "CAC_CERTIFICATE",
      "fileName": "cac_certificate.pdf",
      "fileSizeBytes": 512000,
      "mimeType": "application/pdf",
      "uploadedAt": "2026-05-16T09:00:00Z",
      "presignedUrl": "documents/kyb/kyb_abc123/1715853000000-cac_certificate.pdf?X-Amz-Algorithm=..."
    }
  ]
}
```

***

### Get KYB Document Download URL

`GET /api/v1/kyc/kyb/applications/:id/documents/:docId/download`

Get a time-limited presigned URL to download a KYB document.

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": {
    "url": "documents/kyb/kyb_abc123/1715853000000-cac_certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256...",
    "expiresIn": 300
  }
}
```

***

### Delete KYB Document

`DELETE /api/v1/kyc/kyb/applications/:id/documents/:docId`

Delete a KYB document from MinIO and the database. Requires `BANK_ADMIN` or `COMPLIANCE_OFFICER` role.

**Example Response — 200 OK**

```json theme={null}
{
  "success": true,
  "data": { "deleted": true }
}
```
