Overview
The Transactions module is the entry point for all AML/CFT screening. Every financial transaction your institution processes should be submitted to the/screen endpoint. The platform returns a verdict in under 200ms with a risk score, outcome, and triggered rules.
Common Workflows
Real-time screening: Your core banking system calls/screen on every outgoing transfer → The API returns an outcome → Your system routes the transaction based on APPROVE, REVIEW, ESCALATE, or BLOCK.
Post-hoc review: A compliance officer queries the transaction list to review recent screenings, drill into a specific transaction, and view the full verdict breakdown.
Permissions
| Action | Who Can Do It |
|---|---|
| Screen transaction | All authenticated users |
| List transactions | All authenticated users |
| Get transaction detail | All authenticated users |
Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/transactions/screen | Submit a transaction for screening |
GET | /api/v1/transactions | List screened transactions |
GET | /api/v1/transactions/:id | Retrieve a specific transaction |
Screen Transaction
Submit a single transaction for real-time screening. The response includes the final outcome, risk breakdown per engine, triggered rules, and recommended actions. Request Body| Field | Type | Required | Description |
|---|---|---|---|
externalId | string | ✅ | Your unique transaction reference |
type | string | ✅ | CASH_DEPOSIT, CASH_WITHDRAWAL, TRANSFER, INTERNATIONAL_TRANSFER, POS, ATM, MOBILE, USSD, INTERNET_BANKING |
channel | string | ✅ | BRANCH, MOBILE_APP, INTERNET_BANKING, POS_TERMINAL, ATM, USSD, AGENT, API |
amount | number | ✅ | Amount in base currency unit (e.g. 5000000 = ₦5M) |
currency | string | ISO 4217 code. Defaults to NGN | |
senderAccountNumber | string | ✅ | Sender’s bank account number (NUBAN, 10 digits) |
senderBvn | string | Sender’s BVN. Enables identity risk scoring | |
senderKycStatus | string | VERIFIED, PENDING, IN_PROGRESS, REJECTED, EXPIRED, NONE | |
senderKycTier | string | TIER_1, TIER_2, TIER_3. Required in EXTERNAL mode; optional in HYBRID mode | |
senderKycVerifiedAt | string | ISO 8601 timestamp of last KYC verification | |
senderKycExternalRef | string | Internal KYC reference (e.g. CORE-BANK-KYC-78432) | |
senderName | string | ✅ | Sender’s full name. Screened against sanctions |
senderBankCode | string | Sender’s bank CBN code (e.g. "058" for GTBank) | |
receiverAccountNumber | string | Receiver’s account number | |
receiverName | string | Receiver’s full name. Also screened | |
receiverKycStatus | string | VERIFIED, PENDING, IN_PROGRESS, REJECTED, EXPIRED, NONE | |
receiverKycVerifiedAt | string | ISO 8601 timestamp of last KYC verification | |
receiverKycExternalRef | string | Internal KYC reference for the receiver | |
receiverBvn | string | Receiver’s BVN | |
receiverBankCode | string | Receiver’s bank CBN code (e.g. "058") | |
receiverCountry | string | ISO 3166-1 alpha-2. Triggers FTR if ≠ NG | |
narration | string | Transaction description | |
deviceId | string | Device fingerprint for behavioral analysis | |
ipAddress | string | Client IP for geo-anomaly detection | |
latitude | number | GPS latitude. Used for geo-anomaly detection | |
longitude | number | GPS longitude. Used for geo-anomaly detection | |
timestamp | string | ✅ | ISO 8601 timestamp |
metadata | object | Optional metadata for business transactions (see KYB Screening) |
curl -X POST /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",
"senderKycStatus": "VERIFIED",
"senderKycTier": "TIER_2",
"senderKycVerifiedAt": "2026-05-01T10:00:00Z",
"senderKycExternalRef": "CORE-BANK-KYC-78432",
"senderName": "John Doe",
"senderBankCode": "058",
"receiverAccountNumber": "9876543210",
"receiverBvn": "22087654321",
"receiverName": "Jane Smith",
"receiverBankCode": "033",
"receiverCountry": "NG",
"receiverKycStatus": "PENDING",
"receiverKycVerifiedAt": "2026-04-15T09:30:00Z",
"receiverKycExternalRef": "CORE-BANK-KYC-99123",
"narration": "Payment for goods",
"deviceId": "device_abc123",
"ipAddress": "102.89.47.12",
"latitude": 6.5244,
"longitude": 3.3792,
"timestamp": "2026-05-16T12:00:00Z",
"metadata": {
"entityType": "INDIVIDUAL"
}
}'
const response = await fetch("/v1/transactions/screen", {
method: "POST",
headers: {
Authorization: "Bearer <access_token>",
"Content-Type": "application/json",
},
body: JSON.stringify({
externalId: "TXN-2026-001",
type: "TRANSFER",
channel: "MOBILE_APP",
amount: 5000000,
currency: "NGN",
senderAccountNumber: "0123456789",
senderBvn: "22012345678",
senderKycStatus: "VERIFIED",
senderKycTier: "TIER_2",
senderKycVerifiedAt: "2026-05-01T10:00:00Z",
senderKycExternalRef: "CORE-BANK-KYC-78432",
senderName: "John Doe",
senderBankCode: "058",
receiverAccountNumber: "9876543210",
receiverBvn: "22087654321",
receiverName: "Jane Smith",
receiverBankCode: "033",
receiverCountry: "NG",
receiverKycStatus: "PENDING",
receiverKycVerifiedAt: "2026-04-15T09:30:00Z",
receiverKycExternalRef: "CORE-BANK-KYC-99123",
narration: "Payment for goods",
deviceId: "device_abc123",
ipAddress: "102.89.47.12",
latitude: 6.5244,
longitude: 3.3792,
timestamp: "2026-05-16T12:00:00Z",
metadata: {
entityType: "INDIVIDUAL",
},
}),
});
const result = await response.json();
console.log(result.data.outcome); // "APPROVE" | "REVIEW" | "ESCALATE" | "BLOCK"
import requests
response = requests.post(
"/v1/transactions/screen",
headers={
"Authorization": "Bearer <access_token>",
"Content-Type": "application/json"
},
json={
"externalId": "TXN-2026-001",
"type": "TRANSFER",
"channel": "MOBILE_APP",
"amount": 5000000,
"currency": "NGN",
"senderAccountNumber": "0123456789",
"senderBvn": "22012345678",
"senderKycStatus": "VERIFIED",
"senderKycTier": "TIER_2",
"senderKycVerifiedAt": "2026-05-01T10:00:00Z",
"senderKycExternalRef": "CORE-BANK-KYC-78432",
"senderName": "John Doe",
"senderBankCode": "058",
"receiverAccountNumber": "9876543210",
"receiverBvn": "22087654321",
"receiverName": "Jane Smith",
"receiverBankCode": "033",
"receiverCountry": "NG",
"receiverKycStatus": "PENDING",
"receiverKycVerifiedAt": "2026-04-15T09:30:00Z",
"receiverKycExternalRef": "CORE-BANK-KYC-99123",
"narration": "Payment for goods",
"deviceId": "device_abc123",
"ipAddress": "102.89.47.12",
"latitude": 6.5244,
"longitude": 3.3792,
"timestamp": "2026-05-16T12:00:00Z",
"metadata": {
"entityType": "INDIVIDUAL"
}
}
)
result = response.json()
print(result["data"]["outcome"]) # "APPROVE" | "REVIEW" | "ESCALATE" | "BLOCK"
{
"success": true,
"data": {
"transactionId": "fae50ecb-d997-4700-bae7-49650678bb06",
"externalId": "TXN-0791-389803",
"outcome": "BLOCK",
"riskLevel": "HIGH",
"aggregateScore": 100,
"actions": ["NOTIFY_OFFICER", "CREATE_CASE"],
"riskBreakdown": [
{
"category": "Regulatory Compliance",
"score": 0,
"outcome": "APPROVE",
"rulesTriggered": 0,
"latencyMs": 3
},
{
"category": "Sanctions & PEP Screening",
"score": 0,
"outcome": "APPROVE",
"rulesTriggered": 0,
"latencyMs": 3020
},
{
"category": "KYC Verification",
"score": 100,
"outcome": "BLOCK",
"rulesTriggered": 1,
"latencyMs": 0
},
{
"category": "Custom Rules",
"score": 0,
"outcome": "APPROVE",
"rulesTriggered": 0,
"latencyMs": 10074
},
{
"category": "Decision Engine",
"score": 0,
"outcome": "APPROVE",
"rulesTriggered": 0,
"latencyMs": 48
},
{
"category": "Behavioral Analysis",
"score": 0,
"outcome": "APPROVE",
"rulesTriggered": 1,
"latencyMs": 206
}
],
"triggeredRules": [
{
"name": "KYC Status Non-Verified",
"category": "KYC Verification",
"riskScore": 100,
"details": "No KYC record found for sender (BVN: 22075678966)",
"createdBy": null,
"kycSource": "DATABASE",
"kycExternalRef": null
}
],
"totalLatencyMs": 13447
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "field": "amount", "message": "amount must be a positive number" }
]
}
}
{
"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
Retrieve a paginated list of all screened transactions in your tenant. Query Parameters| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Items per page (max 100) |
outcome | string | — | Filter by APPROVE, REVIEW, ESCALATE, BLOCK |
type | string | — | Filter by transaction type |
from | string | — | ISO 8601 start date |
to | string | — | ISO 8601 end date |
curl -X GET "/v1/transactions?outcome=BLOCK&page=1&limit=10" \
-H "Authorization: Bearer <access_token>"
{
"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
Retrieve the full screening result for a single transaction, including the complete verdict with engine breakdowns. Path Parameters| Parameter | Type | Description |
|---|---|---|
id | string | Transaction UUID |
curl -X GET /v1/transactions/fae50ecb-d997-4700-bae7-49650678bb06 \
-H "Authorization: Bearer <access_token>"
{
"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": [
{
"category": "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 Trust Modes
Tenants can configure how KYC status is resolved during transaction screening:| Mode | Behavior |
|---|---|
STRICT | Default. KYC records must exist in the DB. Payload KYC is ignored. |
HYBRID | Uses payload KYC if provided, falls back to DB lookup. |
EXTERNAL | Requires KYC status in every transaction payload. No local DB lookup. |
PATCH /api/v1/tenants/me with kycTrustMode.