Licensed API
VerifyNow AML
Enrol an identified person or company, run the first screen, then rescreen against refreshed lists on your RMCP cadence. Withdraw when the relationship ends. Enrolment is API-first.
This is the licensed ongoing AML and ODD API. One-shot AML/PEP screening stays on POST /aml-screening. Product overview: VerifyNow AML.
Licensed path
POST /api/external/aml-ongoingAuthentication
Send a hashed, approved organisation API key in x-api-key.
Credits
First enrol uses AML/PEP credits. Same-subject rescreens on an active licence use 0 credits.
Access
Annual licence. Production uses a hashed, approved organisation API key. Ongoing rescreens need an active licence.
Request a licenceAuthentication
Production calls use an organisation API key that is hashed at rest, approved, and tied to an active VerifyNow AML licence. Send the key in x-api-key. Production requests also send a unique Idempotency-Key between 8 and 160 characters. Reuse that key only when retrying the same body.
Set mode to sandbox for mock results. Sandbox calls use 0 credits. Production ongoing rescreens require an active licence.
Enrol and rescreen
POST the customer name plus identifier. The first call enrols the subject and runs the onboarding screen. The same payload rescreens that enrolled subject. Read operation in the response: onboarding for the first enrol, ongoing for a same-subject rescreen on an active licence.
| Field | Value |
|---|---|
| name | Person or company name, 2 to 300 characters |
| identifierType | sa_id or passport for a person; cipc, lei, or tax for a company |
| identifierValue | The ID, passport, CIPC, LEI, or tax number |
| entity | 0 Person, 1 Company, 2 Organization, 3 LegalEntity |
| country | Two-letter ISO country code |
| dataset | all, sanctions, peps, or crime |
| mode | sandbox or production |
| identifierCountry | Optional two-letter ISO code. sa_id, cipc, and tax default to ZA |
Person enrol
curl -X POST https://www.verifynow.co.za/api/external/aml-ongoing \
-H "x-api-key: $VERIFYNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"mode": "production",
"name": "SAMPLE MATCH PERSON",
"entity": 0,
"country": "za",
"dataset": "all",
"identifierType": "sa_id",
"identifierValue": "8001015009087"
}'Company enrol
curl -X POST https://www.verifynow.co.za/api/external/aml-ongoing \
-H "x-api-key: $VERIFYNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"mode": "production",
"name": "SAMPLE MATCH COMPANY",
"entity": 1,
"country": "za",
"dataset": "all",
"identifierType": "cipc",
"identifierValue": "202012345607"
}'Production path is POST /api/external/aml-ongoing. Access still requires a hashed, approved organisation API key. Ongoing rescreens also need an active licence.
Identifiers
Ongoing CDD attaches later screens to the same identified customer. Send the name and identifier on every rescreen.
| Field | Value |
|---|---|
| Person · sa_id | South African ID number. Format, date, citizenship, and checksum are checked. |
| Person · passport | 5 to 20 letters or digits. Send identifierCountry. |
| Company · cipc | CIPC registration number, 8 to 14 digits. |
| Company · lei | 20 alphanumeric characters. |
| Company · tax | 9 or 10 digits. South African tax numbers default to ZA. |
Responses
Customer responses keep match evidence. Read results.Success, results.SearchResults.Found, and RecordCount. Sample JSON uses synthetic names and matches the product page.
Request
{
"mode": "production",
"name": "SAMPLE MATCH PERSON",
"entity": 0,
"country": "za",
"dataset": "all",
"identifierType": "sa_id",
"identifierValue": "8001015009087"
}Positive match · operation onboarding
{
"success": true,
"requestId": "enrol-sample-match-01",
"results": {
"Success": true,
"ResponseCode": 200,
"Messages": [
"Possible financial-crime matches found"
],
"Header": {
"SearchDate": "2026-08-17T08:00:00.000Z",
"ReportName": "SAMPLE MATCH PERSON",
"ReportReference": "amlmon_enrolsamplematch0100000000000000",
"ClientReference": "enrol-sample-match-01",
"ReportType": "Ongoing Financial Crime Monitoring",
"AuditReference": "amlmon_enrolsamplematch0100000000000000",
"DatasetVersion": "aml_20260817_sample",
"DatasetPublishedAt": "2026-08-17T07:12:00.000Z",
"ScreenedAt": "2026-08-17T08:00:00.000Z",
"CategoriesRequested": [
"sanctions",
"crime",
"wanted",
"regulatory",
"debarment",
"pep",
"rca"
],
"DataOrigin": "publisher_direct"
},
"SearchResults": {
"InputName": "SAMPLE MATCH PERSON",
"InputMinScore": "90",
"InputIdentifierMatchBoostingThreshold": "",
"InputCountry": "ZA",
"Found": true,
"Message": "Possible financial-crime matches found",
"RecordCount": 1,
"EntityMatchCount": 1,
"JurisdictionRiskCount": 0,
"HasNextPage": false
},
"PossibleMatches": {
"Found": true,
"Message": "Possible financial-crime matches found",
"HasNextPage": false,
"Results": [
{
"Name": "SAMPLE MATCH PERSON",
"AltNames": [
"SAMPLE MATCH ALIAS"
],
"EntityId": "vn-a1b2c3d4e5f6789012345678",
"EntityType": "Person",
"CountryResidence": [
"ZA"
],
"DateOfBirth": [
"1980-01-01"
],
"Identifiers": [],
"Address": [],
"ConfidenceScore": 0.972,
"RiskCategories": [
"sanctions"
],
"Topics": [
"sanction"
],
"MatchExplanations": {
"name_match": {
"score": 1,
"weight": 1,
"impact": 1,
"query": "SAMPLE MATCH PERSON",
"candidate": "SAMPLE MATCH PERSON",
"detail": "Best comparison across the submitted name and source aliases."
}
},
"SourceEvidence": [
{
"Name": "US OFAC Specially Designated Nationals List",
"ShortName": "us_ofac_sdn",
"RecordReference": "sample-sdn-0001",
"ListVersion": "us_ofac_sdn:sample",
"PublishedAt": "2026-08-17",
"DataOrigin": "publisher_direct",
"Attribution": null
}
],
"FieldEvidence": {
"lastName": [
{
"value": "PERSON",
"source": "us_ofac_sdn",
"recordReference": "sample-sdn-0001",
"listVersion": "us_ofac_sdn:sample"
}
]
},
"DataSource": {
"Name": "US OFAC Specially Designated Nationals List",
"ShortName": "us_ofac_sdn",
"RecordReference": "sample-sdn-0001",
"ListVersion": "us_ofac_sdn:sample",
"PublishedAt": "2026-08-17",
"DataOrigin": "publisher_direct",
"Attribution": null
},
"Program": [
"SDGT"
],
"ListedOn": "2024-01-15",
"Designation": "Sample designation for documentation",
"Comments": "Linked To: SAMPLE MATCH ENTITY",
"Remarks": "Linked To: SAMPLE MATCH ENTITY Listed on 2024-01-15",
"remarks": "Linked To: SAMPLE MATCH ENTITY Listed on 2024-01-15",
"sanctions": "Linked To: SAMPLE MATCH ENTITY Listed on 2024-01-15",
"Relationships": [],
"name": "SAMPLE MATCH PERSON",
"aliases": "SAMPLE MATCH ALIAS",
"schema": "Person",
"entity_type": "Person",
"countries": "ZA",
"birth_date": "1980-01-01",
"addresses": "",
"identifiers": "",
"score": 0.972,
"matched_name": "SAMPLE MATCH PERSON",
"source": "US OFAC Specially Designated Nationals List",
"source_key": "us_ofac_sdn",
"source_record_id": "sample-sdn-0001",
"source_version": "us_ofac_sdn:sample",
"dataset": "default",
"dataset_version": "aml_20260817_sample"
}
]
},
"JurisdictionRisks": {
"Found": false,
"Message": "No jurisdiction risk indicators found",
"RecordCount": 0,
"Results": []
}
},
"remainingCredits": 1840,
"screened_name": "SAMPLE MATCH PERSON",
"screened_entity_type": "Person",
"screened_country": "ZA",
"screened_dataset": "all",
"operation": "onboarding"
}Clear / negative · operation ongoing
{
"success": true,
"requestId": "rescreen-sample-clear-01",
"results": {
"Success": true,
"ResponseCode": 200,
"Messages": [
"No possible matches found in the categories checked"
],
"Header": {
"SearchDate": "2026-08-17T08:00:00.000Z",
"ReportName": "SAMPLE MATCH PERSON",
"ReportReference": "amlmon_rescreensampleclear0100000000000",
"ClientReference": "rescreen-sample-clear-01",
"ReportType": "Ongoing Financial Crime Monitoring",
"AuditReference": "amlmon_rescreensampleclear0100000000000",
"DatasetVersion": "aml_20260817_sample",
"DatasetPublishedAt": "2026-08-17T07:12:00.000Z",
"ScreenedAt": "2026-08-17T08:00:00.000Z",
"CategoriesRequested": [
"sanctions",
"crime",
"wanted",
"regulatory",
"debarment",
"pep",
"rca"
],
"DataOrigin": "publisher_direct"
},
"SearchResults": {
"InputName": "SAMPLE MATCH PERSON",
"InputMinScore": "90",
"InputIdentifierMatchBoostingThreshold": "",
"InputCountry": "ZA",
"Found": false,
"Message": "No possible matches found in the categories checked",
"RecordCount": 0,
"EntityMatchCount": 0,
"JurisdictionRiskCount": 0,
"HasNextPage": false
},
"PossibleMatches": {
"Found": false,
"Message": "No possible matches found in the categories checked",
"HasNextPage": false,
"Results": []
},
"JurisdictionRisks": {
"Found": false,
"Message": "No jurisdiction risk indicators found",
"RecordCount": 0,
"Results": []
}
},
"remainingCredits": 1840,
"screened_name": "SAMPLE MATCH PERSON",
"screened_entity_type": "Person",
"screened_country": "ZA",
"screened_dataset": "all",
"operation": "ongoing"
}Withdraw
Remove the subject from the active monitoring population when the relationship ends. Send the same identifier used at enrol. Earlier screening evidence stays available for your FICA record period.
POST /api/external/aml-ongoing/withdrawRequest
curl -X POST https://www.verifynow.co.za/api/external/aml-ongoing/withdraw \
-H "x-api-key: $VERIFYNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"entity": 0,
"identifierType": "sa_id",
"identifierValue": "8001015009087"
}'Response
{
"success": true,
"withdrawn": true,
"subjectId": "3d4d83b0-0f76-4b58-9e6b-6b45cfac2db2"
}Dataset webhook and email
Register an HTTPS webhook URL with your licence. When a list version changes, VerifyNow sends aml_monitoring.dataset_updated with an HMAC-SHA256 signature. Organisation owner and admin contacts also receive an email. TFS source changes are highlighted in that email.
These are notifications. Enrolled subjects are not rescreened automatically. POST the same name and identifier after the webhook or email, or on the review dates in your RMCP.
Webhook payload
{
"id": "evt_amlmon_aml_20260817_sample",
"type": "aml_monitoring.dataset_updated",
"createdAt": "2026-08-17T07:12:00.000Z",
"data": {
"datasetVersion": "aml_20260817_sample",
"publishedAt": "2026-08-17T07:12:00.000Z",
"recordCount": 184000,
"changes": [
{
"source": "us_ofac_sdn",
"title": "US OFAC Specially Designated Nationals List",
"added": 12,
"updated": 3,
"removed": 2
}
],
"sourceStatus": "current",
"dataOrigin": "publisher_direct"
}
}x-verifynow-event-idStable ID for duplicate handlingx-verifynow-timestampUnix timestamp in secondsx-verifynow-signaturev1=<hex HMAC-SHA256>Verify the signature
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const secret = process.env.VERIFYNOW_WEBHOOK_SECRET;
const seenEvents = new Set();
app.post(
'/webhooks/verifynow-aml',
express.raw({ type: 'application/json' }),
(req, res) => {
const rawBody = req.body.toString('utf8');
const timestamp = req.header('x-verifynow-timestamp') || '';
const received = req.header('x-verifynow-signature') || '';
const eventId = req.header('x-verifynow-event-id') || '';
const expected =
'v1=' +
crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + rawBody)
.digest('hex');
const validLength = expected.length === received.length;
const validSignature =
validLength &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
if (!validSignature || !fresh) return res.sendStatus(401);
if (seenEvents.has(eventId)) return res.sendStatus(200);
seenEvents.add(eventId);
const event = JSON.parse(rawBody);
if (event.type === 'aml_monitoring.dataset_updated') {
// Rescreen enrolled subjects on your RMCP cadence.
}
return res.sendStatus(200);
},
);
app.use(express.json());
app.listen(3000);Errors
A new enrol that would exceed the licensed population returns HTTP 403. The licence size stays as contracted. The API does not auto-upgrade.
{
"error": "POPULATION_LIMIT_EXCEEDED",
"message": "Licensed monitoring population exceeded"
}| Field | Value |
|---|---|
| 401 | Missing or invalid x-api-key |
| 400 | Invalid body, identifier, dataset, or missing Idempotency-Key |
| 402 INSUFFICIENT_CREDITS | First enrol needs AML/PEP credits on the API-key balance |
| 403 POPULATION_LIMIT_EXCEEDED | Licensed monitoring population is full |
| 403 AML_LICENCE_REQUIRED | Active VerifyNow AML licence required for ongoing rescreens |
| 429 RATE_LIMITED | Retry after the Retry-After header |
| 202 pending | A screen for that key is already in progress. Retry after Retry-After |
Credits and licence
First enrol
The first screen for a name and identifier uses the same credits as AML/PEP screening.
Same-subject rescreen
Later screens of that enrolled identifier on an active licence use 0 credits.
Annual licence
Population is sized to your customer book. Request a licence.
See the main API reference for shared authentication, credits, and idempotency rules.