VerifyNow

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

Authentication

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 licence

Authentication

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.

FieldValue
namePerson or company name, 2 to 300 characters
identifierTypesa_id or passport for a person; cipc, lei, or tax for a company
identifierValueThe ID, passport, CIPC, LEI, or tax number
entity0 Person, 1 Company, 2 Organization, 3 LegalEntity
countryTwo-letter ISO country code
datasetall, sanctions, peps, or crime
modesandbox or production
identifierCountryOptional 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.

FieldValue
Person · sa_idSouth African ID number. Format, date, citizenship, and checksum are checked.
Person · passport5 to 20 letters or digits. Send identifierCountry.
Company · cipcCIPC registration number, 8 to 14 digits.
Company · lei20 alphanumeric characters.
Company · tax9 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/withdraw

Request

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 handling
x-verifynow-timestampUnix timestamp in seconds
x-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"
}
FieldValue
401Missing or invalid x-api-key
400Invalid body, identifier, dataset, or missing Idempotency-Key
402 INSUFFICIENT_CREDITSFirst enrol needs AML/PEP credits on the API-key balance
403 POPULATION_LIMIT_EXCEEDEDLicensed monitoring population is full
403 AML_LICENCE_REQUIREDActive VerifyNow AML licence required for ongoing rescreens
429 RATE_LIMITEDRetry after the Retry-After header
202 pendingA 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.