AML screening

Retrieving the AML result

Poll or receive the webhook, then read the result correctly.

Two ways to get the result

WayHow
WebhookThe DATABASE_CHECKS_COMPLETE event reaches the callback URL of the scenario once all checks of the session are terminal. It is one POST with the full result set. See webhooks and receiving webhooks.
PollingCall database check results with the session identifier every 5 to 10 seconds until checksStatus is complete.

The two carry the same checks, but not the same shape. See the two shapes.

When to stop

  • A session. complete needs the session to be terminal as well as every check. While the session is idle or processing, more checks may still be enqueued, so even an empty list reports pending. An abandoned session never reaches complete: its stored status stays idle, and the expiry is not written to the record. Stop polling when the session lifetime that you set in the scenario has passed.
  • A questionnaire record. complete needs the record to be committed (status of success), no check pending and the set of checks settled. A record has no time to live, so wait for the webhook instead of polling to a deadline.
  • A scenario with no checks answers complete with an empty results. That is how "nothing was checked" differs from an unknown identifier, which answers session not found.

The webhook can repeat

The event always carries the full result set. It is sent again whenever the session returns to a fully terminal state. Treat it as idempotent by sessionId: replace what you stored for that session with the latest set, and do not count events.

Privacy options

Each terminal check result is sent in full whatever the option that omits personal data, because without the verdict a skipped check would read as clean even on a match. The option that encrypts the body applies to the webhook. Polling always returns the full result in plain form and works without a callback URL.

The request

curl -X POST "<API_BASE_URL>/kyc/session/databases" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "<your permanent API token>",
    "sessionId": "00000000-0000-0000-0000-000000000000"
  }'

The two shapes

Polling returns the verdict of each check under result. The webhook has no task-status wrapper and puts the same fields one level flatter, on the entry itself.

Polling (kyc/session/databases)Webhook (DATABASE_CHECKS_COMPLETE)
Top levelsessionId, checksStatus, pending, results, responseTimeevent, sessionId, schemaId, clientId, results, responseTime
Entry of resultschecker, status, outcome, notPerformedReason, missingInputs, spent, and the verdict under resultchecker, spent, outcome, notPerformedReason, missingInputs and the verdict fields directly: passed, score, verified, items, totalCount, details, error
Verdictresults[].result.passedresults[].passed

Normalize both into one shape before you apply your decision logic:

function normalize(check) {
  const verdict = check.result ?? check;   // polling nests the verdict under "result"; the webhook does not
  return {
    checker: check.checker,
    outcome: check.outcome,
    passed: verdict.passed,
    items: verdict.items,
    totalCount: verdict.totalCount,
  };
}

How to read a result

Read outcome before passed.

outcomeMeaning
performedThe source was queried and the result is meaningful.
skippedThe check did not run because input data was missing or unusable. An empty result means not checked, not clean.
unavailableThe source failed or could not be reached. A re-run can succeed.
nullThe check is not finished yet.

notPerformedReason gives the reason when there is no result: missingInput, unusableInput, identityUnresolved, ageBelowDocumentAge, providerUnavailable, providerError or unknown. For missingInput, supply the data that missingInputs names and run again.

When outcome is performed, the verdict is carried by result.passed:

passedMeaning
trueThe check passed: the person was not found in a risk list, or validation succeeded.
falseA match was found, or validation failed.
nullAn error or a skip.

The matched records are in result.items, their count is in result.totalCount, and the error text, if any, is in result.error.

A skipped check looks successful

A check that never ran arrives with status of success and passed of null. That is how the source reports a skip. If you branch on status alone, a check that never ran looks like a success. Branch on outcome.

for (const entry of results) {
  const check = normalize(entry);
  if (check.outcome !== "performed") {
    // not checked: missing input, or the source was unavailable
    continue;
  }
  if (check.passed === false) {
    // a match was found: send to manual review
  }
}

The status of a check

The status field is the status of the check task: idle, processing, success, failed or exception. See session statuses.

Personal data erasure

After an erasure request the object keeps its identifiers, statuses and verdicts and carries no personal values. See erase session personal data.

Questions about screening?

Write to us with the details and we will help.

Talk to us