Skip to main content
Version: 1.521.0

Webhooks

This document outlines the webhook functionality in the TrustChex system.

Overview​

Webhooks allow your application to receive real-time notifications about events happening in your TrustChex account. When an event occurs, we'll send a HTTP POST request to the configured webhook URL.

Key Features:

  • Real-time event notifications
  • Automatic retry mechanism with exponential backoff
  • HMAC SHA256 signature verification for security
  • Comprehensive event logging and monitoring
  • Test functionality for easy debugging

Event Types​

Verification Session Events​

  • verification_session.created - A new verification session has been created
  • verification_session.completed - A verification session has been completed
  • verification_session.cancelled - A verification session has been cancelled

Identification Events​

  • identification.created - A new identification has been created
  • identification.submitted - An identification has been submitted for review
  • identification.failed - An identification has failed processing
  • identification.in_automatic_review - An identification is under automatic review
  • identification.in_manual_review - An identification requires manual review
  • identification.waiting_for_status_change_approval - Status change requires approval
  • identification.approved - An identification has been approved
  • identification.rejected - An identification has been rejected
  • identification.resubmission_required - An identification requires resubmission

Managing Webhooks​

Creating a Webhook​

  1. Navigate to Settings > Webhooks in your dashboard
  2. Click "New Webhook"
  3. Configure your webhook:
    • URL: The endpoint where you want to receive webhook events
    • Description: Optional description for your webhook
    • Events: Select which event types you want to subscribe to
    • Active: Enable/disable the webhook
  4. Click "Save" to create your webhook

Testing Webhooks​

Use the test button in the webhook dashboard to verify your endpoint:

  1. Click the Test button next to your webhook
  2. A test event will be sent to your endpoint
  3. Check the event logs to see the delivery status
  4. Review the request/response details for debugging

Webhook Payload​

All webhooks include the following standard fields:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"accountId": "your-account-id",
"webhookId": "webhook-id",
"eventType": "identification.approved",
"timestamp": "2024-01-01T12:00:00.000Z",
"data": {
"accountId": "your-account-id",
"identificationId": "identification-id",
"status": "APPROVED",
"email": "user@example.com",
"phoneNumber": "+1234567890",
"score": 95,
"createdAt": "2024-01-01T11:00:00.000Z",
"updatedAt": "2024-01-01T12:00:00.000Z"
}
}

Event-Specific Payload Examples​

Verification Session Data Structure​

{
"data": {
"accountId": "your-account-id",
"sessionId": "session-id",
"status": "COMPLETED",
"identificationId": "identification-id",
"email": "user@example.com",
"phoneNumber": "+1234567890",
"locale": "en",
"sendOTP": true,
"createdAt": "2024-01-01T11:00:00.000Z",
"updatedAt": "2024-01-01T12:00:00.000Z",
"expiresAt": "2024-01-01T13:00:00.000Z"
}
}

Identification Data Structure​

{
"data": {
"accountId": "your-account-id",
"identificationId": "identification-id",
"status": "IN_MANUAL_REVIEW",
"email": "user@example.com",
"phoneNumber": "+1234567890",
"score": 70,
"reasons": [
{ "code": "AML_POTENTIAL_HIT", "effect": "MANUAL_REVIEW", "step": "AML_CHECK", "detail": { "matchScore": 88 } }
],
"createdAt": "2024-01-01T11:00:00.000Z",
"updatedAt": "2024-01-01T12:00:00.000Z"
}
}

Decision Reasons​

identification.approved, identification.rejected and identification.in_manual_review events include reasons, the checks that shaped the automatic decision. The field is left out for identifications decided before reasons were introduced, so treat it as optional. An approved identification usually has an empty list.

Each reason has:

  • code: a stable identifier (below). New codes may be added, so ignore codes you don't recognize.
  • effect: what the reason did.
    • REJECT: rejected on its own.
    • MANUAL_REVIEW: forced manual review.
    • SCORE_WITHHELD: the step's score wasn't awarded.
    • INFO: advisory only.
  • step (optional): the workflow step type it came from.
  • detail (optional): small non-personal details such as verdicts, counts or scores.
CodeMeaning
DOCUMENT_MISSINGNo document was captured for a document step
HOLOGRAM_NOT_VERIFIEDThe document hologram couldn't be verified
SECONDARY_FACE_MISMATCHThe ghost portrait doesn't match the main portrait
PERSONAL_NUMBER_INVALIDThe personal number failed validation
BARCODE_MISMATCHThe barcode doesn't match the document data
UNDERAGEThe holder is under 18
DOCUMENT_EXPIREDThe document is expired
APPROVAL_CRITERIA_NOT_MET / APPROVAL_CRITERIA_REJECTEDSession approval criteria were not met (the second means the criterion rejects)
NATIONALITY_BLOCKED / NATIONALITY_NOT_ALLOWEDNationality is blocked, or isn't on the allowed list
DOCUMENT_NOT_FROM_CHIPAn eID step's data wasn't read from the chip
CHIP_VIZ_MISMATCHChip data doesn't match the printed page
PASSIVE_AUTH_FAILEDChip Passive/Active Authentication failed, or the signing certificate is revoked
ACTIVE_AUTH_NOT_PERFORMEDActive Authentication is required but wasn't performed
FACE_MISMATCHThe selfie doesn't match the document portrait
FACE_MATCH_UNAVAILABLEThe face comparison couldn't be run
SPOOF_DETECTEDA presentation attack was detected
LIVENESS_FAILEDThe liveness check failed
CONSENT_MISSING / VERBAL_CONSENT_MISSINGContract or verbal consent is missing
ADDRESS_MISSINGNo address was captured
AML_HIT / AML_POTENTIAL_HIT / AML_INCONCLUSIVESanctions screening outcome
VIDEO_CALL_NOT_COMPLETED / VIDEO_CALL_NO_OPERATORThe video call didn't complete, or had no operator
FOREIGN_PASSPORT_VIDEO_CALL_REQUIREDA foreign-national passport needs an operator video call
DEVICE_EMULATORCaptured on an emulator
DOCUMENT_REUSEDThe same document was used in other recent identifications (detail.otherAttempts)
SCORE_BELOW_AUTO_APPROVAL / SCORE_BELOW_MANUAL_REVIEWThe total score is below the workflow threshold

Headers​

Each webhook request includes these headers:

  • Content-Type: application/json
  • User-Agent: TrustChex-Webhook/1.0
  • X-Webhook-Event: {eventType}
  • X-Webhook-Delivery: {deliveryId}
  • X-Webhook-Timestamp: {timestamp}
  • X-Webhook-Secret: {secret} (if configured)
  • X-Webhook-Signature-256: sha256={signature} (if secret configured)

Security​

Secret Verification​

If you configure a secret for your webhook, we'll include it in the X-Webhook-Secret header and generate a HMAC SHA256 signature in the X-Webhook-Signature-256 header.

To verify the signature:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}

Delivery and Retries​

  • Webhooks have a 30-second timeout
  • Failed deliveries are retried up to 3 times with exponential backoff (1s, 3s, 9s)
  • HTTP status codes 2xx are considered successful
  • All webhook attempts are logged and can be viewed in the dashboard

Testing​

Use the test functionality in the webhook dashboard to send a test event to your endpoint:

{
"id": "test-delivery-id",
"accountId": "your-account-id",
"webhookId": "your-webhook-id",
"eventType": "test.webhook",
"timestamp": "2024-01-01T12:00:00.000Z",
"data": {
"message": "This is a test webhook delivery",
"test": true
}
}

Test Endpoint​

For testing purposes, you can use our public webhook endpoint:

https://your-domain.com/api/public/webhooks

Query parameters:

  • ?fail=true - Simulate a failure (returns 500)
  • ?status=404 - Return specific status code
  • ?delay=5000 - Add delay in milliseconds (max 60000)

Best Practices​

  1. Idempotency: Use the webhook delivery ID to avoid processing duplicate events
  2. Timeout Handling: Respond within 30 seconds to avoid timeouts
  3. Error Handling: Return appropriate HTTP status codes (2xx for success)
  4. Security: Always verify webhook signatures when using secrets
  5. Logging: Log webhook events for debugging and monitoring
  6. Graceful Degradation: Handle webhook failures gracefully in your application

Example Implementations​

Node.js Express​

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature-256'];
const secret = process.env.WEBHOOK_SECRET;

if (secret && signature) {
const payload = JSON.stringify(req.body);
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
return res.status(401).send('Unauthorized');
}
}

console.log('Webhook received:', req.body);

// Process the webhook event
switch (req.body.eventType) {
case 'identification.approved':
// Handle approval
break;
case 'identification.rejected':
// Handle rejection
break;
default:
console.log('Unknown event type:', req.body.eventType);
}

res.json({ received: true });
});

Python Flask​

import hmac
import hashlib
import json
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature-256')
secret = os.environ.get('WEBHOOK_SECRET')

if secret and signature:
payload = request.get_data()
expected_signature = 'sha256=' + hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()

if not hmac.compare_digest(signature, expected_signature):
return jsonify({'error': 'Unauthorized'}), 401

data = request.get_json()
print(f"Webhook received: {data}")

# Process the webhook event
event_type = data.get('eventType')
if event_type == 'identification.approved':
# Handle approval
pass
elif event_type == 'identification.rejected':
# Handle rejection
pass

return jsonify({'received': True})