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:
Authentication
Transaction Screening
KYC Verification
KYB Verification
Liveness Check
Health & System
How Screening Works
Webhooks
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
Field Type Required Description externalIdstring✅ Your unique transaction reference. Used for idempotency — sending the same ID twice returns the cached verdict. typestring✅ TRANSFER, CASH_DEPOSIT, CASH_WITHDRAWAL, INTERNATIONAL_TRANSFER, BILL_PAYMENTchannelstring✅ MOBILE_APP, INTERNET_BANKING, USSD, ATM, POS, BRANCH, API, AGENTamountnumber✅ Amount in base currency unit (e.g. 5000000 = ₦5M). currencystringISO 4217 code. Defaults to NGN. senderAccountNumberstring✅ Sender’s bank account number (NUBAN, 10 digits). senderBvnstringSender’s BVN. Strongly recommended — enables KYC Verification scoring. senderNamestring✅ Sender’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. timestampstring✅ ISO 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
Parameter Type Default Description pageinteger1Page number limitinteger20Items per page (max 100) outcomestring— Filter by APPROVE, REVIEW, ESCALATE, BLOCK typestring— Filter by transaction type fromstring— ISO 8601 start date tostring— ISO 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
Parameter Type Description 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
Status Meaning Risk Impact PENDINGSubmitted, awaiting verification Score 40, REVIEW DOCUMENT_UPLOADEDID documents uploaded Score 20, APPROVE NIN_VERIFIEDNIN check passed Score 0, APPROVE BVN_VERIFIEDBVN check passed Score 0, APPROVE LIVENESS_PASSEDBiometric liveness check passed Score 0, APPROVE APPROVEDFully verified by officer Score 0, APPROVE REJECTEDFailed verification Score 70, ESCALATE EXPIREDVerification expired Score 50, REVIEW
Submit KYC Application
POST /api/v1/kyc/applications
Create a new KYC application for an individual customer.
Request Body
Field Type Required Description entityTypestring✅ INDIVIDUAL or BUSINESSfirstNamestring✅ Applicant first name lastNamestring✅ Applicant 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
Parameter Type Default Description statusstring— Filter 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
Parameter Type Description idstringKYC Application UUID
Request Body
Field Type Required Description bvnstring✅ BVN to verify (11 digits) firstNamestring✅ First name for cross-match lastNamestring✅ Last name for cross-match dateOfBirthstring✅ Date 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
Field Type Required Description ninstring✅ NIN to verify (11 digits) firstNamestring✅ First name for cross-match lastNamestring✅ Last name for cross-match dateOfBirthstring✅ Date 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
Liveness Detection: The provider analyzes a selfie image to detect presentation attacks (printed photos, screens, masks, deepfakes).
Face Match (optional): If a document image is provided, the system compares the selfie face against the document photo.
Status Update: On success, the application status advances to LIVENESS_PASSED.
When to Use
Workflow When to Call Standard KYC After BVN/NIN verification, before officer approval High-risk onboarding As a mandatory step for politically exposed persons Re-verification When KYC expires and the customer needs to re-verify
Path Parameters
Parameter Type Description idstringKYC Application UUID
Request Body
Field Type Required Description selfieImageBase64string✅ Base64-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
Field Type Required Description 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
Field Type Required Description reasonstring✅ Reason 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
Status Meaning Risk Impact PENDINGApplication submitted, awaiting CAC lookup Score 40, REVIEW CAC_VERIFIEDCAC lookup successful Score 40, REVIEW DIRECTORS_VERIFIEDDirectors identity verified Score 40, REVIEW BOS_VERIFIEDBeneficial owners screened Score 10, APPROVE APPROVEDFully verified Score 0, APPROVE REJECTEDFailed verification or sanctions hit Score 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
Field Type Required Description companyNamestring✅ Registered company name rcNumberstringCAC registration number tinstringTax Identification Number addressstringRegistered business address incorporationDatestringISO 8601 date directorsarrayArray of director objects directors[].namestring✅ Director full name directors[].bvnstringDirector BVN directors[].ninstringDirector NIN directors[].emailstringDirector email directors[].phonestringDirector phone directors[].nationalitystringDirector nationality beneficialOwnersarrayArray of beneficial owner objects beneficialOwners[].namestring✅ Owner full name beneficialOwners[].percentagenumber✅ Ownership 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
Field Type Required Description rcNumberstring✅ CAC 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
Field Type Required Description directorsarray✅ Array of director verification objects directors[].namestring✅ Director 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
Field Type Required Description beneficialOwnersarray✅ Array of BO screening objects beneficialOwners[].namestring✅ Owner name beneficialOwners[].cleanboolean✅ Pre-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
Field Type Required Description reasonstring✅ Reason 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
Stage Engine Latency What It Does 1 Validation ~5ms Schema validation, idempotency check, persist transaction 2 Context Enrichment ~15ms Fetch sender’s recent history, account dormancy, KYC/KYB status 3 Global Sanctions Screening ~25ms Screen names against OFAC, UN, EU, UK, Interpol, PEP lists 4 Regulatory Compliance ~2ms Execute 7 mandatory AML rules 5 Identity Risk (KYC) ~1ms Evaluate sender’s KYC verification status 6 Identity Risk (KYB) ~1ms For business transactions — evaluate KYB verification status 7 Decision Engine ~10ms Local rule evaluation using stored AST formulas 8 Behavioral Analysis ~50ms ML scoring for device, velocity, and geographic anomalies
Mandatory Regulatory Rules
These 7 rules are always active and cannot be disabled :
Rule Trigger Auto-Actions AML-001 Cash ThresholdCash ≥ ₦5,000,000 Generate CTR, Notify Officer AML-002 Transfer ThresholdTransfer ≥ ₦10,000,000 Generate CTR, Notify Officer AML-003 StructuringSingle txn ≥ 80% of threshold, OR 3+ txns in 24h exceeding threshold Generate SAR, Create Case AML-004 PEP ScreeningSender is a Politically Exposed Person Enhanced Due Diligence AML-005 Foreign TransferInternational transfer or receiver country ≠ NG Generate FTR AML-006 Dormant AccountNo activity for ≥ 180 days Create Case AML-007 Rapid Succession≥ 5 txns within 60 minutes from same account Create Case
Identity Risk Scoring
Individual (KYC)
Sender KYC Status Risk Score Outcome APPROVED 0 APPROVE NIN_VERIFIED / BVN_VERIFIED / LIVENESS_PASSED 0 APPROVE PENDING / DOCUMENT_UPLOADED 20 APPROVE NONE (no application)40 REVIEW EXPIRED 50 REVIEW REJECTED 70 ESCALATE
Business (KYB)
Sender KYB Status Risk Score Outcome APPROVED 0 APPROVE BOS_VERIFIED 10 APPROVE PENDING / CAC_VERIFIED / DIRECTORS_VERIFIED 40 REVIEW NONE (no application)60 REVIEW REJECTED 85 BLOCK
Score Aggregation
Category Weight Rationale Regulatory Compliance 1.5× Core compliance — highest authority Identity Verification 1.3× KYC/KYB integrity is compliance-critical Custom Rules 1.2× Institution-specific logic elevated above generic engines Global Sanctions Screening 1.0× Standard weight — severity via outcome escalation Decision Engine 1.0× Rule-mirroring redundancy Behavioral Analysis 0.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
Event Trigger 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
Property Value Max Attempts 5 Backoff Strategy Exponential (5s → 10s → 20s → 40s → 80s) Request Timeout 10 seconds per attempt Completed Job Retention 24 hours Failed Job Retention 7 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.
Node.js (Express)
Python (Flask)
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:
Code Meaning Solution 200Success Request processed successfully. 201Created Resource created successfully. 400Bad Request Check validation errors in the response body. Ensure all required fields are present. 401Unauthorized Your token or API key is missing, invalid, or revoked. 403Forbidden Your role does not have permission for this action. 409Conflict Duplicate externalId or conflicting state. 429Too Many Requests Rate limit exceeded — implement exponential backoff. 500Server Error Unexpected 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 .