Skip to main content

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.

Introduction

Verifow is an enterprise-grade Anti-Money Laundering (AML) and Combating the Financing of Terrorism (CFT) platform built for African financial institutions. Our API enables you to:
  • Screen transactions through a multi-layer compliance pipeline in under 200ms
  • Verify identities via BVN, NIN, and biometric liveness checks
  • Onboard businesses with CAC lookup, director verification, and beneficial owner screening
  • Monitor compliance via real-time dashboards and webhook notifications
This guide covers everything you need to integrate:
  1. Authentication
  2. Transaction Screening
  3. KYC Verification
  4. KYB Verification
  5. Liveness Check
  6. Health & System
  7. How Screening Works
  8. Webhooks
  9. Error Handling

Authentication

The API supports two authentication methods. All endpoints (except /health) require authentication.

Method 1: Bearer Token (Dashboard Sessions)

Used when calling the API from the dashboard or on behalf of a logged-in user.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Obtain a token by calling POST /api/v1/auth/login with email and password.

Method 2: API Key (Backend Integrations)

Used for server-to-server integrations from your core banking system.
X-API-Key: rk_live_a1b2c3d4e5f6g7h8i9j0
Generate API keys from Dashboard → API Keys. API keys are tenant-scoped and carry the same permissions as the user who created them.
Keep your credentials secret. Never expose tokens or API keys in client-side code (mobile apps, browser JavaScript). All API calls must originate from your backend servers.

Transaction Screening

Screen Transaction

POST /api/v1/transactions/screen Submit a single transaction for real-time AML/CFT screening. The response includes the final outcome, risk breakdown per engine, triggered rules, and recommended actions. Request Body
FieldTypeRequiredDescription
externalIdstringYour unique transaction reference. Used for idempotency — sending the same ID twice returns the cached verdict.
typestringTRANSFER, CASH_DEPOSIT, CASH_WITHDRAWAL, INTERNATIONAL_TRANSFER, BILL_PAYMENT
channelstringMOBILE_APP, INTERNET_BANKING, USSD, ATM, POS, BRANCH, API, AGENT
amountnumberAmount in base currency unit (e.g. 5000000 = ₦5M).
currencystringISO 4217 code. Defaults to NGN.
senderAccountNumberstringSender’s bank account number (NUBAN, 10 digits).
senderBvnstringSender’s BVN. Strongly recommended — enables KYC Verification scoring.
senderNamestringSender’s full name. Screened against global sanctions & PEP lists.
senderBankCodestringSender’s bank CBN code (e.g. "058" for GTBank).
receiverAccountNumberstringReceiver’s account number.
receiverNamestringReceiver’s full name. Also screened if provided.
receiverBvnstringReceiver’s BVN.
receiverBankCodestringReceiver’s bank CBN code.
receiverCountrystringISO 3166-1 alpha-2 code (e.g. "NG", "US"). Triggers FTR if ≠ NG.
narrationstringTransaction description. Scanned for high-risk keywords.
deviceIdstringDevice fingerprint. Enables behavioral anomaly detection.
ipAddressstringClient IP address. Enables geo-anomaly detection.
latitudenumberGPS latitude. Used for geo-anomaly detection.
longitudenumberGPS longitude. Used for geo-anomaly detection.
timestampstringISO 8601 (e.g. "2026-05-08T12:00:00Z").
metadataobjectOptional metadata for business transactions (see KYB Screening).
Example Request — Individual Transaction
curl -X POST /api/v1/transactions/screen \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "TXN-2026-001",
    "type": "TRANSFER",
    "channel": "MOBILE_APP",
    "amount": 5000000,
    "currency": "NGN",
    "senderAccountNumber": "0123456789",
    "senderBvn": "22012345678",
    "senderName": "John Doe",
    "receiverAccountNumber": "9876543210",
    "receiverName": "Jane Smith",
    "receiverCountry": "NG",
    "narration": "Payment for goods",
    "timestamp": "2026-05-08T12:00:00Z"
  }'
Example Response — 200 OK (REVIEW)
{
  "success": true,
  "data": {
    "transactionId": "fae50ecb-d997-4700-bae7-49650678bb06",
    "externalId": "TXN-2026-001",
    "outcome": "REVIEW",
    "riskLevel": "MEDIUM",
    "aggregateScore": 32,
    "actions": ["NOTIFY_OFFICER"],
    "riskBreakdown": [
      {
        "category": "Regulatory Compliance",
        "score": 0,
        "outcome": "APPROVE",
        "rulesTriggered": 0,
        "latencyMs": 3
      },
      {
        "category": "Global Sanctions Screening",
        "score": 0,
        "outcome": "APPROVE",
        "rulesTriggered": 0,
        "latencyMs": 97
      },
      {
        "category": "KYC Verification",
        "score": 40,
        "outcome": "REVIEW",
        "rulesTriggered": 1,
        "latencyMs": 0
      },
      {
        "category": "Custom Rules",
        "score": 0,
        "outcome": "APPROVE",
        "rulesTriggered": 0,
        "latencyMs": 178
      },
      {
        "category": "Decision Engine",
        "score": 0,
        "outcome": "APPROVE",
        "rulesTriggered": 0,
        "latencyMs": 9
      },
      {
        "category": "Behavioral Analysis",
        "score": 0,
        "outcome": "APPROVE",
        "rulesTriggered": 1,
        "latencyMs": 92
      }
    ],
    "triggeredRules": [
      {
        "name": "No KYC Application Found",
        "category": "KYC Verification",
        "riskScore": 40,
        "details": "No KYC record found for sender (BVN: 22012345678)",
        "createdBy": null
      }
    ],
    "totalLatencyMs": 398
  }
}
Example Response — 200 OK (BLOCK with Sanctions Hit)
{
  "success": true,
  "data": {
    "transactionId": "20442e74-c039-4640-aadd-5ee5d9024b33",
    "externalId": "TXN-2026-002",
    "outcome": "BLOCK",
    "riskLevel": "CRITICAL",
    "aggregateScore": 95,
    "actions": [
      "CREATE_CASE",
      "NOTIFY_COMPLIANCE",
      "GENERATE_SAR",
      "ENHANCED_DUE_DILIGENCE"
    ],
    "riskBreakdown": [
      {
        "category": "Regulatory Compliance",
        "score": 100,
        "outcome": "BLOCK",
        "rulesTriggered": 2,
        "latencyMs": 1
      },
      {
        "category": "Global Sanctions Screening",
        "score": 95,
        "outcome": "BLOCK",
        "rulesTriggered": 2,
        "latencyMs": 97
      }
    ],
    "triggeredRules": [
      {
        "name": "Structuring Detection",
        "category": "Regulatory Compliance",
        "riskScore": 80,
        "details": "Potential structuring: 4 transactions totaling ₦11,000,000.00 in 24h",
        "createdBy": null
      },
      {
        "name": "Sender \"John Doe\" matches global sanctions list",
        "category": "Global Sanctions Screening",
        "riskScore": 95,
        "details": "Matched: \"LAM, John Top\" (95.0% confidence) | Source: US OFAC Sanctions | Programs: GLOMAG",
        "createdBy": null
      }
    ],
    "totalLatencyMs": 398
  }
}
Example Response — 400 Bad Request
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      { "field": "amount", "message": "amount must be a positive number" }
    ]
  }
}
Example Response — 409 Conflict (Idempotent)
{
  "success": false,
  "error": {
    "code": "DUPLICATE_EXTERNAL_ID",
    "message": "This externalId has already been processed",
    "data": {
      "transactionId": "fae50ecb-d997-4700-bae7-49650678bb06",
      "outcome": "REVIEW",
      "riskLevel": "MEDIUM",
      "aggregateScore": 32
    }
  }
}
Idempotency: Sending the same externalId twice returns the cached verdict without re-screening. This makes retries safe.

List Transactions

GET /api/v1/transactions Retrieve a paginated list of all screened transactions in your tenant. Query Parameters
ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Items per page (max 100)
outcomestringFilter by APPROVE, REVIEW, ESCALATE, BLOCK
typestringFilter by transaction type
fromstringISO 8601 start date
tostringISO 8601 end date
Example Response — 200 OK
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "fae50ecb-d997-4700-bae7-49650678bb06",
        "externalId": "TXN-0791-389803",
        "type": "TRANSFER",
        "channel": "MOBILE_APP",
        "amount": "5000000",
        "currency": "NGN",
        "senderAccountNumber": "0123456789",
        "senderName": "John Doe",
        "receiverName": "Jane Smith",
        "receiverCountry": "NG",
        "transactionTimestamp": "2026-05-16T12:00:00Z",
        "verdict": {
          "outcome": "BLOCK",
          "riskLevel": "CRITICAL",
          "aggregateScore": 95,
          "totalLatencyMs": 145
        },
        "createdAt": "2026-05-16T12:00:00Z"
      }
    ],
    "total": 3,
    "page": 1,
    "limit": 10,
    "totalPages": 1
  }
}

Get Transaction Detail

GET /api/v1/transactions/:id Retrieve the full screening result for a single transaction, including the complete verdict with engine breakdowns. Path Parameters
ParameterTypeDescription
idstringTransaction UUID
Example Response — 200 OK
{
  "success": true,
  "data": {
    "id": "fae50ecb-d997-4700-bae7-49650678bb06",
    "tenantId": "660e8400-e29b-41d4-a716-446655440001",
    "externalId": "TXN-0791-389803",
    "type": "TRANSFER",
    "channel": "MOBILE_APP",
    "amount": "5000000",
    "currency": "NGN",
    "senderAccountNumber": "0123456789",
    "senderBvn": "22012345678",
    "senderName": "John Doe",
    "senderBankCode": null,
    "receiverAccountNumber": "9876543210",
    "receiverBvn": null,
    "receiverName": "Jane Smith",
    "receiverBankCode": null,
    "receiverCountry": "NG",
    "narration": "Payment for goods",
    "deviceId": null,
    "ipAddress": null,
    "latitude": null,
    "longitude": null,
    "transactionTimestamp": "2026-05-16T12:00:00Z",
    "metadata": null,
    "createdAt": "2026-05-16T12:00:00Z",
    "verdict": {
      "id": "vtx_001",
      "transactionId": "fae50ecb-d997-4700-bae7-49650678bb06",
      "outcome": "REVIEW",
      "riskLevel": "MEDIUM",
      "aggregateScore": 32,
      "engineVerdicts": [
        {
          "engineName": "KYC Verification",
          "score": 40,
          "outcome": "REVIEW",
          "rulesTriggered": 1,
          "latencyMs": 0
        }
      ],
      "actions": ["NOTIFY_OFFICER"],
      "totalLatencyMs": 13447,
      "processedAt": "2026-05-16T12:00:01Z"
    }
  }
}
Transaction data is immutable once screened. If you need to re-evaluate, submit a new transaction with a different externalId.

KYC Verification

KYC (Know Your Customer) verification validates individual customer identities through BVN, NIN, and biometric liveness checks against national identity providers.

KYC Application Statuses

StatusMeaningRisk Impact
PENDINGSubmitted, awaiting verificationScore 40, REVIEW
DOCUMENT_UPLOADEDID documents uploadedScore 20, APPROVE
NIN_VERIFIEDNIN check passedScore 0, APPROVE
BVN_VERIFIEDBVN check passedScore 0, APPROVE
LIVENESS_PASSEDBiometric liveness check passedScore 0, APPROVE
APPROVEDFully verified by officerScore 0, APPROVE
REJECTEDFailed verificationScore 70, ESCALATE
EXPIREDVerification expiredScore 50, REVIEW

Submit KYC Application

POST /api/v1/kyc/applications Create a new KYC application for an individual customer. Request Body
FieldTypeRequiredDescription
entityTypestringINDIVIDUAL or BUSINESS
firstNamestringApplicant first name
lastNamestringApplicant last name
bvnstringBank Verification Number (11 digits)
ninstringNational Identity Number (11 digits)
phonestringMobile number with country code
emailstringEmail address
addressstringResidential address
dateOfBirthstringISO 8601 date (e.g. "1990-03-15")
Example Request
curl -X POST /api/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",
    "address": "12 Lagos Street, Lagos",
    "dateOfBirth": "1990-03-15"
  }'
Example Response — 201 Created
{
  "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",
    "address": "12 Lagos Street, Lagos",
    "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"
  }
}

List KYC Applications

GET /api/v1/kyc/applications Query Parameters
ParameterTypeDefaultDescription
statusstringFilter by status
pageinteger1Page number
limitinteger20Items per page

Get KYC Application

GET /api/v1/kyc/applications/:id Retrieve a KYC application with full verification results and uploaded documents. Example Response — 200 OK
{
  "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",
    "address": "12 Lagos Street, Lagos",
    "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

POST /api/v1/kyc/applications/:id/verify-bvn Verify the applicant’s BVN against the configured identity provider (Prembly, Dojah, or BYOL). Path Parameters
ParameterTypeDescription
idstringKYC Application UUID
Request Body
FieldTypeRequiredDescription
bvnstringBVN to verify (11 digits)
firstNamestringFirst name for cross-match
lastNamestringLast name for cross-match
dateOfBirthstringDate of birth for cross-match
Example Request
curl -X POST /api/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
{
  "success": true,
  "data": {
    "isMatch": true,
    "confidence": 0.95,
    "provider": "PREMBLY",
    "newStatus": "BVN_VERIFIED"
  }
}

Verify NIN

POST /api/v1/kyc/applications/:id/verify-nin Verify the applicant’s NIN against the configured identity provider. Request Body
FieldTypeRequiredDescription
ninstringNIN to verify (11 digits)
firstNamestringFirst name for cross-match
lastNamestringLast name for cross-match
dateOfBirthstringDate of birth for cross-match
Example Response — 200 OK
{
  "success": true,
  "data": {
    "isMatch": true,
    "confidence": 0.94,
    "provider": "PREMBLY",
    "newStatus": "NIN_VERIFIED"
  }
}

Liveness Check

POST /api/v1/kyc/applications/:id/liveness-check Run a biometric liveness detection and face-match check. This verifies that the applicant is a real person (not a photo or video spoof) and optionally matches their selfie against an uploaded ID document.

How It Works

  1. Liveness Detection: The provider analyzes a selfie image to detect presentation attacks (printed photos, screens, masks, deepfakes).
  2. Face Match (optional): If a document image is provided, the system compares the selfie face against the document photo.
  3. Status Update: On success, the application status advances to LIVENESS_PASSED.

When to Use

WorkflowWhen to Call
Standard KYCAfter BVN/NIN verification, before officer approval
High-risk onboardingAs a mandatory step for politically exposed persons
Re-verificationWhen KYC expires and the customer needs to re-verify
Path Parameters
ParameterTypeDescription
idstringKYC Application UUID
Request Body
FieldTypeRequiredDescription
selfieImageBase64stringBase64-encoded selfie image (JPEG/PNG). Must show the applicant’s full face with good lighting.
documentImageBase64stringBase64-encoded ID document image (passport, driver’s license, national ID). Used for face-match comparison.
Example Request
curl -X POST /api/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 (Pass)
{
  "success": true,
  "data": {
    "isLive": true,
    "confidence": 0.97,
    "faceMatch": true,
    "faceMatchConfidence": 0.92,
    "provider": "PREMBLY",
    "newStatus": "LIVENESS_PASSED"
  }
}
Example Response — 200 OK (Fail — Spoof Detected)
{
  "success": true,
  "data": {
    "isLive": false,
    "confidence": 0.12,
    "faceMatch": false,
    "faceMatchConfidence": 0.0,
    "provider": "PREMBLY",
    "newStatus": "PENDING"
  }
}
A failed liveness check does not automatically reject the application. The status remains PENDING (or its previous state) so a compliance officer can review and decide whether to request a retry or reject.

Approve KYC Application

PATCH /api/v1/kyc/applications/:id/approve Final approval of a KYC application. Requires BANK_ADMIN or COMPLIANCE_OFFICER role. Request Body
FieldTypeRequiredDescription
notesstringOfficer notes
Example Response — 200 OK
{
  "success": true,
  "data": {
    "id": "kyc_abc123",
    "status": "APPROVED",
    "riskLevel": "LOW",
    "notes": "All documents verified. BVN and NIN match.",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}
Approving a KYC application updates the customer’s risk profile. Future transactions from this customer will reflect the APPROVED status (score 0, no KYC-related risk).

Reject KYC Application

PATCH /api/v1/kyc/applications/:id/reject Reject a KYC application that fails verification. Requires BANK_ADMIN or COMPLIANCE_OFFICER role. Request Body
FieldTypeRequiredDescription
reasonstringReason for rejection
Example Response — 200 OK
{
  "success": true,
  "data": {
    "id": "kyc_abc123",
    "status": "REJECTED",
    "riskLevel": "HIGH",
    "notes": "BVN name mismatch. Uploaded document appears fraudulent.",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}

KYB Verification

KYB (Know Your Business) verifies corporate entities before they transact. It integrates with the Corporate Affairs Commission (CAC) to validate company registration, verify directors through KYC identity checks, and screen beneficial owners against global sanctions lists.

KYB Application Statuses

StatusMeaningRisk Impact
PENDINGApplication submitted, awaiting CAC lookupScore 40, REVIEW
CAC_VERIFIEDCAC lookup successfulScore 40, REVIEW
DIRECTORS_VERIFIEDDirectors identity verifiedScore 40, REVIEW
BOS_VERIFIEDBeneficial owners screenedScore 10, APPROVE
APPROVEDFully verifiedScore 0, APPROVE
REJECTEDFailed verification or sanctions hitScore 85, BLOCK

KYB Screening Trigger

KYB checks run automatically during transaction screening when the transaction metadata indicates a business entity:
{
  "metadata": {
    "entityType": "BUSINESS",
    "senderRcNumber": "RC123456"
  }
}

Submit KYB Application

POST /api/v1/kyc/kyb/applications Create a new KYB application for a corporate entity. Request Body
FieldTypeRequiredDescription
companyNamestringRegistered company name
rcNumberstringCAC registration number
tinstringTax Identification Number
addressstringRegistered business address
incorporationDatestringISO 8601 date
directorsarrayArray of director objects
directors[].namestringDirector full name
directors[].bvnstringDirector BVN
directors[].ninstringDirector NIN
directors[].emailstringDirector email
directors[].phonestringDirector phone
directors[].nationalitystringDirector nationality
beneficialOwnersarrayArray of beneficial owner objects
beneficialOwners[].namestringOwner full name
beneficialOwners[].percentagenumberOwnership percentage
beneficialOwners[].bvnstringOwner BVN
beneficialOwners[].ninstringOwner NIN
Example Request
curl -X POST /api/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" }
    ]
  }'
Example Response — 201 Created
{
  "success": true,
  "data": {
    "id": "kyb_abc123",
    "tenantId": "660e8400-e29b-41d4-a716-446655440001",
    "companyName": "Acme Nigeria Ltd",
    "rcNumber": "RC123456",
    "tin": "12345678-0001",
    "address": "15 Broad Street, Lagos",
    "incorporationDate": "2015-06-12T00:00:00Z",
    "status": "PENDING",
    "riskLevel": null,
    "directors": [
      { "name": "John Doe", "bvn": "22012345678", "email": "john@acme.ng" }
    ],
    "beneficialOwners": [
      { "name": "Jane Smith", "percentage": 75, "bvn": "22087654321" }
    ],
    "assignedTo": null,
    "notes": null,
    "createdAt": "2026-05-16T08:00:00Z",
    "updatedAt": "2026-05-16T08:00:00Z"
  }
}

Verify CAC

POST /api/v1/kyc/kyb/applications/:id/verify-cac Run a CAC lookup by RC number to fetch company details, directors, and beneficial owners. Request Body
FieldTypeRequiredDescription
rcNumberstringCAC registration number
Example Response — 200 OK
{
  "success": true,
  "data": {
    "found": true,
    "confidence": 0.95,
    "company": {
      "rcNumber": "RC123456",
      "companyName": "Acme Nigeria Ltd",
      "address": "15 Broad Street, Lagos",
      "registrationDate": "2015-06-12",
      "directors": [
        {
          "name": "John Doe",
          "nationality": "Nigerian",
          "appointmentDate": "2015-06-12"
        }
      ],
      "beneficialOwners": [{ "name": "Jane Smith", "percentage": 75 }]
    },
    "newStatus": "CAC_VERIFIED"
  }
}

Verify Directors

POST /api/v1/kyc/kyb/applications/:id/verify-directors Verify each director’s identity via BVN/NIN against identity providers. Request Body
FieldTypeRequiredDescription
directorsarrayArray of director verification objects
directors[].namestringDirector name
directors[].ninstringDirector NIN
directors[].bvnstringDirector BVN
directors[].verifiedbooleanPre-verified flag
Example Response — 200 OK
{
  "success": true,
  "data": {
    "allVerified": true,
    "directors": [
      {
        "name": "John Doe",
        "nin": "12345678901",
        "bvn": "22012345678",
        "verified": true
      }
    ],
    "newStatus": "DIRECTORS_VERIFIED"
  }
}

Screen Beneficial Owners

POST /api/v1/kyc/kyb/applications/:id/screen-beneficial-owners Screen beneficial owners against global sanctions and PEP watchlists. Request Body
FieldTypeRequiredDescription
beneficialOwnersarrayArray of BO screening objects
beneficialOwners[].namestringOwner name
beneficialOwners[].cleanbooleanPre-screened clean flag
beneficialOwners[].hitsarrayList of watchlist hits if not clean
Example Response — 200 OK
{
  "success": true,
  "data": {
    "allClean": true,
    "beneficialOwners": [{ "name": "Jane Smith", "clean": true, "hits": [] }],
    "newStatus": "BOS_VERIFIED"
  }
}

Approve KYB Application

PATCH /api/v1/kyc/kyb/applications/:id/approve Requires BANK_ADMIN or COMPLIANCE_OFFICER role. Example Response — 200 OK
{
  "success": true,
  "data": {
    "id": "kyb_abc123",
    "status": "APPROVED",
    "riskLevel": "LOW",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}

Reject KYB Application

PATCH /api/v1/kyc/kyb/applications/:id/reject Requires BANK_ADMIN or COMPLIANCE_OFFICER role. Request Body
FieldTypeRequiredDescription
reasonstringReason for rejection
Example Response — 200 OK
{
  "success": true,
  "data": {
    "id": "kyb_abc123",
    "status": "REJECTED",
    "riskLevel": "HIGH",
    "notes": "Sanctions hit on beneficial owner Jane Smith.",
    "updatedAt": "2026-05-16T10:00:00Z"
  }
}

Health & System

Health Check

GET /api/v1/health Public endpoint — no authentication required. Use this to monitor system availability. Example Response — 200 OK
{
  "status": "healthy",
  "timestamp": "2026-05-17T02:46:09.295Z",
  "version": "0.1.0",
  "services": {
    "database": "connected"
  }
}

Engine Health

GET /api/v1/engines/health Returns the health status of all connected screening engines. Requires BANK_ADMIN or SUPER_ADMIN. Example Response — 200 OK
{
  "success": true,
  "data": {
    "Decision Engine": {
      "isHealthy": true,
      "latencyMs": 6,
      "details": { "mood": "Feu flammes !" }
    },
    "Behavioral Analysis": {
      "isHealthy": true,
      "version": "latest",
      "latencyMs": 36
    },
    "SANCTIONS_SCREENING": {
      "isHealthy": true,
      "latencyMs": 2,
      "details": {
        "description": "Global Sanctions Screening — OFAC SDN/Non-SDN, EU/UK/UN sanctions, FinCEN 311",
        "endpoint": "http://ratel-watchman:8084/search",
        "sources": "US OFAC, EU FSF, UK HMT, UN SC, US CSL"
      }
    }
  }
}

How Screening Works

Every transaction passes through multiple screening layers in sequence. The entire pipeline completes in 80–200ms (p95).

The Pipeline

StageEngineLatencyWhat It Does
1Validation~5msSchema validation, idempotency check, persist transaction
2Context Enrichment~15msFetch sender’s recent history, account dormancy, KYC/KYB status
3Global Sanctions Screening~25msScreen names against OFAC, UN, EU, UK, Interpol, PEP lists
4Regulatory Compliance~2msExecute 7 mandatory AML rules
5Identity Risk (KYC)~1msEvaluate sender’s KYC verification status
6Identity Risk (KYB)~1msFor business transactions — evaluate KYB verification status
7Decision Engine~10msLocal rule evaluation using stored AST formulas
8Behavioral Analysis~50msML scoring for device, velocity, and geographic anomalies

Mandatory Regulatory Rules

These 7 rules are always active and cannot be disabled:
RuleTriggerAuto-Actions
AML-001 Cash ThresholdCash ≥ ₦5,000,000Generate CTR, Notify Officer
AML-002 Transfer ThresholdTransfer ≥ ₦10,000,000Generate CTR, Notify Officer
AML-003 StructuringSingle txn ≥ 80% of threshold, OR 3+ txns in 24h exceeding thresholdGenerate SAR, Create Case
AML-004 PEP ScreeningSender is a Politically Exposed PersonEnhanced Due Diligence
AML-005 Foreign TransferInternational transfer or receiver country ≠ NGGenerate FTR
AML-006 Dormant AccountNo activity for ≥ 180 daysCreate Case
AML-007 Rapid Succession≥ 5 txns within 60 minutes from same accountCreate Case

Identity Risk Scoring

Individual (KYC)

Sender KYC StatusRisk ScoreOutcome
APPROVED0APPROVE
NIN_VERIFIED / BVN_VERIFIED / LIVENESS_PASSED0APPROVE
PENDING / DOCUMENT_UPLOADED20APPROVE
NONE (no application)40REVIEW
EXPIRED50REVIEW
REJECTED70ESCALATE

Business (KYB)

Sender KYB StatusRisk ScoreOutcome
APPROVED0APPROVE
BOS_VERIFIED10APPROVE
PENDING / CAC_VERIFIED / DIRECTORS_VERIFIED40REVIEW
NONE (no application)60REVIEW
REJECTED85BLOCK

Score Aggregation

CategoryWeightRationale
Regulatory Compliance1.5×Core compliance — highest authority
Identity Verification1.3×KYC/KYB integrity is compliance-critical
Custom Rules1.2×Institution-specific logic elevated above generic engines
Global Sanctions Screening1.0×Standard weight — severity via outcome escalation
Decision Engine1.0×Rule-mirroring redundancy
Behavioral Analysis0.8×Supplementary evidence
Score Floor Guarantee: Final Score = MAX(weighted_average, max_engine_score × 0.8) The highest-severity outcome from any engine becomes the final decision.

Webhooks

Verifow sends real-time notifications to your backend when a compliance officer resolves a case linked to one of your transactions.
Setup: Configure your Webhook URL and Webhook Secret in the Developer Webhooks section of the API Keys page in your Dashboard.

Events

EventTrigger
case.resolvedA compliance officer marks a case as RESOLVED_TRUE_POSITIVE, RESOLVED_FALSE_POSITIVE, or CLOSED

Payload

{
  "event": "case.resolved",
  "timestamp": "2026-05-08T15:00:00Z",
  "data": {
    "caseId": "case_12345abcde",
    "status": "RESOLVED_FALSE_POSITIVE",
    "resolutionNote": "Customer provided valid source of funds documentation.",
    "relatedTransactionId": "TXN-2026-001",
    "updatedAt": "2026-05-08T15:00:00Z"
  }
}

Delivery Guarantees

PropertyValue
Max Attempts5
Backoff StrategyExponential (5s → 10s → 20s → 40s → 80s)
Request Timeout10 seconds per attempt
Completed Job Retention24 hours
Failed Job Retention7 days
Your endpoint must return a 2xx status code. Any non-2xx response will trigger automatic retries. If all 5 attempts fail, the event is retained in the dead-letter queue for 7 days.

Signature Verification

Every webhook includes an X-Ratel-Signature header — an HMAC-SHA256 hash of the raw JSON body, signed with your Webhook Secret.
const crypto = require('crypto');

app.post('/ratel/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-ratel-signature'];
  const hash = crypto
    .createHmac('sha256', process.env.RATEL_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (`sha256=${hash}` !== signature) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);

  if (event.event === 'case.resolved') {
    if (event.data.status === 'RESOLVED_FALSE_POSITIVE') {
      // Unfreeze the transaction in your system
    } else if (event.data.status === 'RESOLVED_TRUE_POSITIVE') {
      // Permanently block and escalate
    }
  }

  res.status(200).send('OK');
});

Error Handling

Standard HTTP status codes indicate success or failure:
CodeMeaningSolution
200SuccessRequest processed successfully.
201CreatedResource created successfully.
400Bad RequestCheck validation errors in the response body. Ensure all required fields are present.
401UnauthorizedYour token or API key is missing, invalid, or revoked.
403ForbiddenYour role does not have permission for this action.
409ConflictDuplicate externalId or conflicting state.
429Too Many RequestsRate limit exceeded — implement exponential backoff.
500Server ErrorUnexpected error — retry with backoff.
Fail-Open Policy: If the Sanctions or Behavioral engines experience upstream downtime, Verifow implements a fail-open policy — your legitimate transactions continue processing without interruption, while partial screenings are logged for retroactive review.

Verifow — Enterprise-grade AML/CFT compliance for African financial institutions. For support, contact adams@bevars.com.