Webhooks allow your application to receive automated HTTP POST callbacks when specific events happen, such as a verification session completing or an AML match being found. This eliminates the need to poll the API for status changes.
Webhook Events
Configure which events trigger webhooks from your dashboard. The following events are available:
| Event | Description |
|---|
session.completed | A verification session has finished processing and a decision is available. |
session.approved | A session was approved (identity verified successfully). |
session.declined | A session was declined due to failed checks or suspicious data. |
session.expired | A session expired before the user completed verification. |
aml.completed | An AML screening has finished processing. |
aml.match_found | An AML screening found potential matches against watchlists or PEP databases. |
transaction.flagged | A transaction was flagged for manual review by the risk engine. |
transaction.blocked | A transaction was automatically blocked based on configured rules. |
Webhook Payload
{
"event": "session.completed",
"data": {
"session_id": "ses_a1b2c3d4e5f6",
"status": "approved",
"workflow_id": "wf_abc123",
"risk_score": 12,
"completed_at": "2026-03-15T14:30:00Z"
},
"timestamp": "2026-03-15T14:30:01Z",
"webhook_id": "wh_evt_9f8e7d6c5b4a"
}
| Field | Type | Description |
|---|
event | string | The event type that triggered this webhook. |
data | object | Event-specific data. Contents vary by event type. |
timestamp | string | ISO 8601 timestamp of when the event occurred. |
webhook_id | string | Unique identifier for this webhook delivery. Use for idempotency. |
Signature Verification
Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret key.
Always verify webhook signatures — To prevent spoofed webhook requests, always verify the signature before processing. Your webhook secret is available in your dashboard under API settings.
PHP -- Verify Webhook Signature
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secret = getenv('VERILOCK_WEBHOOK_SECRET');
// Compute expected signature
$expected = hash_hmac('sha256', $payload, $secret);
// Use timing-safe comparison to prevent timing attacks
if (!hash_equals($expected, $signature)) {
http_response_code(401);
echo 'Invalid signature';
exit;
}
// Signature valid — process the event
$event = json_decode($payload, true);
switch ($event['event']) {
case 'session.completed':
handleSessionCompleted($event['data']);
break;
case 'aml.match_found':
handleAmlMatch($event['data']);
break;
case 'transaction.blocked':
handleBlockedTransaction($event['data']);
break;
}
// Always return 200 quickly
http_response_code(200);
echo 'OK';
Retry Policy
If your endpoint does not return a 2xx status code within 10 seconds, Verilock will retry the delivery with exponential backoff:
| Attempt | Delay | Total Elapsed |
|---|
| 1st retry | 10 seconds | ~10s after initial attempt |
| 2nd retry | 60 seconds | ~70s after initial attempt |
| 3rd retry | 300 seconds | ~6 minutes after initial attempt |
After 3 failed retries, the delivery is marked as failed. You can view failed deliveries and manually retry them from your dashboard.
Best Practices
Respond with 200 immediately — Return a 200 status code as soon as you receive the webhook. Process the event asynchronously (e.g., via a job queue) to avoid timeouts.
Handle duplicate deliveries — Use the webhook_id field for idempotency. Store processed webhook IDs and skip duplicates to ensure your logic runs only once per event.
Always verify signatures — Never trust incoming webhook data without first verifying the X-Webhook-Signature header. Use a timing-safe comparison function.