Integrations

Receiving webhooks

A small endpoint that receives events and reads the result.

Webhooks tell your backend when something completes, so that you do not poll. The events, the callback options and the mode that omits identity fields are described on the webhooks page. This guide shows the receiving side.

The events

EventSent when
SESSION_COMPLETEA verification session completes.
QUESTIONNAIRE_COMPLETEA questionnaire step is filled in and committed.
DATABASE_CHECKS_COMPLETEThe database checks of a session, such as AML screening, complete.
PD_ERASURE_COMPLETEThe destruction of personal data requested through the erase call finishes.

Set up the callback

In the scenario callback settings, enter the callback URL of your backend and choose the options. You can encrypt the body of the request with the secret key of the scenario, omit identity fields, notify only when the session is finalized, and send database checks. The DATABASE_CHECKS_COMPLETE event is one notification with the full result set, sent once all checks of the session are terminal. It is not sent check by check.

Treat the event as a signal

Your endpoint receives a POST request with the event. If you chose encryption, decrypt the body with the secret key of the scenario. If you chose to omit identity fields, the callback changes per event:

  • SESSION_COMPLETE: names, document data and images stay out of the callback, and the session identifier stays in. This is the wording of the webhooks page.
  • QUESTIONNAIRE_COMPLETE: the entered values (full name, date of birth, email, phone and the values of your own fields) are dropped. The identifiers stay, and so do the key and the title of each field. The check results of the questionnaire, when the scenario has database checks, are sent in full whatever the option.
  • DATABASE_CHECKS_COMPLETE: every check result is sent in full whatever the option, because without the verdict a skipped check would read as clean even on a match.

The option does not remove check results from a callback. If you chose to encrypt the body, it is encrypted as a whole.

Either way, treat the event as a signal and read the result with the REST API:

  1. Answer the request with a success code quickly. Do your work after answering.
  2. Find the session identifier in the event.
  3. Call session status with that identifier.
  4. For DATABASE_CHECKS_COMPLETE, read the results from the event itself, or call database check results. The event carries the full set every time and is sent again whenever the session returns to a fully terminal state, so store the latest set by sessionId and do not count events.
  5. Make your decision from the server-side status and from the outcome of each check. See session statuses and retrieving the AML result.

A minimal receiver in Node.js

This sketch answers at once and works after answering. It assumes that you did not turn on body encryption. If you did, decrypt the body first. It dispatches on the event:

  • DATABASE_CHECKS_COMPLETE carries event and sessionId. The sketch stores the latest set of results by sessionId, so a repeated event replaces the earlier one.
  • QUESTIONNAIRE_COMPLETE carries event and questionnaireSessionId. Session status accepts that identifier as sessionId and describes the questionnaire record.
  • PD_ERASURE_COMPLETE is ignored here.
  • Any other body is read as a session and carries sessionId.
const express = require("express");
const app = express();
app.use(express.json());

const databaseChecks = new Map();                    // sessionId -> latest results (use your own storage)

async function api(path, body) {
  const reply = await fetch(`${process.env.API_BASE_URL}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ token: process.env.API_TOKEN, ...body }),
  });
  return reply.json();
}

async function handleEvent(event) {
  switch (event.event) {
    case "DATABASE_CHECKS_COMPLETE":
      // Full set every time, and it can arrive again: replace, do not append.
      databaseChecks.set(event.sessionId, event.results);
      return;
    case "QUESTIONNAIRE_COMPLETE":
      console.log(await api("/kyc/session/status", { sessionId: event.questionnaireSessionId }));
      return;
    case "PD_ERASURE_COMPLETE":
      return;
    default: {
      // A session: read the status on the server, then decide.
      const session = await api("/kyc/session/status", { sessionId: event.sessionId });
      console.log(session.status);                   // branch on status
    }
  }
}

app.post("/webhooks/verification", (req, res) => {
  res.sendStatus(200);                               // answer first
  setImmediate(() => handleEvent(req.body).catch(console.error));
});

app.listen(process.env.PORT);

The entries of results in this event have the verdict directly on the entry. See retrieving the AML result for how to normalize them with the polled shape.

Erasure

When you request erasure with erase session personal data, a call that finishes the destruction immediately answers in the same response. If part of it cannot be confirmed in time, the system finishes the work and sends PD_ERASURE_COMPLETE.

Questions, answered plainly

Can I receive callbacks without identity fields?

Yes, for the identity fields. On SESSION_COMPLETE the callback omits names, document data and images and keeps the session identifier. On QUESTIONNAIRE_COMPLETE the entered values are dropped, and on DATABASE_CHECKS_COMPLETE the check results are sent in full whatever the option.

Can I encrypt the callback body?

Yes. Switch on the option to encrypt the body of the callback request with the secret key of the scenario.

Questions about webhooks?

Write to us and we will help.

Talk to us