Webhooks

1. Webhook Configuration

To properly receive incoming webhooks, your server should provide an endpoint supporting:

  • POST requests are either raw in text/plain if encrypted or application/json if no secret was set

You can use a service like e.g. https://webhook.site/ to test receiving webhooks from your developer dashboard.

2. Event Types

Our webhook service supports two events, which describe the status of a flow. Those are:

  • ticket.verification.in_progress: your flow has completed a number of steps (subprocesses) and is waiting for user's intervention to continue to next steps or to retry previous failed one.

  • ticket.verification.completed: your flow has completed its processing. It has been either ACCEPTED or REJECTED.

2.1 Verification - In Progress event

The ticket.verification.in_progress event is triggered when a Verification has completed a number of steps (subprocesses) and it's waiting for user-intervention to continue if it's appropriate. Notification payload specifies a set of properties as shown on following JSON. It will contain its ticket, so it can be tracked by your processes. Also, it holds its event, and its sub_event, which will be explained later. The property flow_status determines ticket's current state. The value of the disposition property can either be PASSED , FAILED or RETRY, which determines last executed subprocess' state. It also specifies how many attempts can be done on property remaining_attempts if disposition has been set as RETRY. Property data contains suprocesses results, which we'll take a look afterwards.

{
  "ticket": "762ebbda-0edb-4e48-86bc-11a280273601",
  "event": "ticket.verification.in_progress",
  "sub_event": "verification.liveness",
  "flow_status": "IN_PROGRESS",
  "disposition": "PASSED",
  "remaining_attempts": 3,
  "data": { ... }
}

In Progress Event disposition statuses

  • PASSED: all subprocesses were executed correctly and their results have been considered as OK.

  • RETRY: this status is set when an exception has been raised, or any subprocess' result has been considered as unacceptable and has retries to attempt, eg. liveness detection process.

  • FAILED, this status is set when an exception has been raised, or any subprocess' result has been considered as unacceptable and no more retries can be atempted, eg. liveness detection process.

Subevents

In this property, we will receive one of both values: verification.liveness and verification.id_proofing

verification.liveness subevent

This subevent is triggered when biometrical subprocesses were executed, which consist of Facial analysis and Liveness detection. In this subevent, property data contains biometrical subprocesses' results. An example is shown bellow:

{
  "ticket": "762ebbda-0edb-4e48-86bc-11a280273601",
  "event": "ticket.verification.in_progress",
  "sub_event": "verification.liveness",
  "flow_status": "IN_PROGRESS",
  "disposition": "PASSED",
  "remaining_attempts": 3,
  "data": {
    "facial_analysis": {
      "status": true,
      "confidence_score": "100",
      "age_range": "22-30"
    },
    "liveness_detection": {
      "status": true,
      "validation_messages": [
        "Liveness detected"
      ]
    }
  }
}

verification.id_proofing subevent

This subevent is triggered when Identity verification subprocesses were executed, which consist of Document detection, Id proofing and Photo matching. In this subevent, property data contains identity verification subprocesses' results. An example is shown bellow:

{
  "ticket": "762ebbda-0edb-4e48-86bc-11a280273601",
  "event": "ticket.verification.in_progress",
  "sub_event": "verification.id_proofing",
  "flow_status": "IN_PROGRESS",
  "disposition": "PASSED",
  "remaining_attempts": 3,
  "data": {
    "document_detection": {
      "status": true
    },
    "id_proofing": {
      "status": true,
      "validations": {
        "recognized_document_type": "VALID",
        "image_quality": "VALID",
        "document_expiry": "VALID",
        "mrz": "VALID",
        "rfid": "NON_VALIDATED_OR_ABSENT",
        "readability": "VALID",
        "security": "VALID",
        "general_status": "VALID",
        "barcode": "VALID",
        "authenticity": "NON_VALIDATED_OR_ABSENT",
        "minimum_required_fields_read": "VALID"
      }
    },
    "photo_matching": {
      "status": true,
      "similarity_score": "99.94"
    }
  }
}

2.2 Verification - Completed event

The ticket.verification.completed event is triggered when a Verification has completed all of its subprocesses, or an error has been raised and there are no more retries to attempt. Notification payload specifies a set of properties as shown on following JSON. It will contain its ticket, so it can be tracked by your processes. Also, it holds its event. The flow_status specified ticket's current state, which can be either ACCEPTED or REJECTED , this value depends on confidence_score property. The value of the disposition property can be either PASSED or FAILED which determines last executed subprocess' state. It also gives final results of entire flow, such as risk_code and confidence_score.

{
  "ticket": "762ebbda-0edb-4e48-86bc-11a280273601",
  "event": "ticket.verification.completed",,
  "flow_status": "ACCEPTED",
  "disposition": "PASSED",
  "remaining_attempts": 3,
  "risk_code": "LOW",
  "confidence_score": 99.99
}

Completed Event flow_status statuses

  • ACCEPTED: all flow's subprocesses were executed correctly and confidence_score has got to ACCEPTED scoring threshold.

  • REJECTED: this status is set when an exception has been raised, or any subprocess' result has been considered as unacceptable and no more retries can be atempted and confidence_score has got to REJECTED scoring threshold.

Completed Event disposition statuses

  • PASSED: all flow's subprocesses were executed correctly and their results have been considered as OK, so ticket is set to COMPLETED state.

  • FAILED, this status is set when an exception has been raised, or any subprocess' result has been considered as unacceptable and no more retries can be atempted, eg. liveness detection process.

Here you find a further description of the response values below, it represents both event responses as these share the same response structure:

3. Security / Encryption

Webhooks are a crucial node to ensure the functionality of your integration, as such, they can be the target of a malicious user aiming to disrupt the service. In order to protect your application from these threats, you must include a 32 bytes-long secret in your webhook configuration, which will be used to encrypt the request body.

When you enable Encryption, the Webhook event is sent as text/plain. Below you'll find an example of how to decipher an incoming webhook request. The cipher initialization vector will be sent over as a 16 bytes metadata (header) of the response.

import * as crypto from 'crypto';

export class Decipher {
  decipherAES_256_CBC(request: any) {
    const CIPHER_KEY = 'YOUR-PLAIN-KEY';
    const BASE64_PLAIN_IV = request.headers['x-pvt-cipher-iv'];
    const BASE64_CIPHERED_MESSAGE = request.body;

    const BUFFER_KEY = Buffer.from(CIPHER_KEY);
    const BUFFER_IV = Buffer.from(BASE64_PLAIN_IV, 'base64');
    const BUFFER_CIPHERED_MESSAGE = Buffer.from(BASE64_CIPHERED_MESSAGE, 'base64');

    const DECIPHER = crypto.createDecipheriv('aes-256-cbc', BUFFER_KEY, BUFFER_IV);
    DECIPHER.setAutoPadding(true);

    let deciphered_message = DECIPHER.update(BUFFER_CIPHERED_MESSAGE, 'hex', 'utf8');

    deciphered_message += DECIPHER.final('utf-8');

    return deciphered_message.toString();
  }
}

Last updated