Identity Verification Webhooks
Webhooks allow your application to receive real-time notifications when events occur in the Trustist platform. Instead of polling for changes, Trustist will POST event data to your specified endpoint.
Overview
When you register a webhook, Trustist will send HTTP POST requests to your URL whenever subscribed events occur. This enables you to:
- Respond immediately to identity verification session completions
- Update your database when verification status changes
- Trigger downstream workflows without polling
- Provide real-time updates to your users
Available Events
| Event | Description | Trigger |
|---|---|---|
onboard.session.completed |
Identity verification session has been successfully completed | Customer finishes all verification steps |
onboard.session.failed |
Identity verification session has failed verification | Customer fails identity or AML checks |
payment.completed,
payment.failed, etc.), see the Payment Webhooks guide.
This page covers webhooks for onboard (identity verification) events only.
Registering a Webhook
Endpoint
/v1/onboard/webhooks
Required Permissions: manage_webhooks
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | The HTTPS URL to receive webhook POSTs |
name |
string | No | Friendly name to identify this webhook |
events |
string[] | No | Event types to subscribe to (defaults to all) |
Example Request
{
"url": "https://yourapp.com/webhooks/trustist",
"name": "Production Webhook",
"events": ["onboard.session.completed"]
}
Example Response
{
"id": "w1234567-89ab-cdef-0123-456789abcdef",
"tenantId": "t1234567-89ab-cdef-0123-456789abcdef",
"url": "https://yourapp.com/webhooks/trustist",
"name": "Production Webhook",
"events": ["onboard.session.completed"],
"isActive": true,
"created": "2025-10-21T10:00:00Z",
"lastUpdated": "2025-10-21T10:00:00Z"
}
Webhook Payload Structure
All webhook events follow a consistent JSON structure:
{
"event": "event.name.here",
"timestamp": "2025-10-21T11:00:00Z",
"data": {
// Event-specific data
}
}
Onboard Session Completed Event
{
"event": "onboard.session.completed",
"timestamp": "2025-10-21T11:00:00Z",
"data": {
"sessionId": "s1234567-89ab-cdef-0123-456789abcdef",
"customerId": "c1234567-89ab-cdef-0123-456789abcdef",
"status": "completed",
"customer": {
"id": "c1234567-89ab-cdef-0123-456789abcdef",
"firstName": "Jane",
"lastName": "Smith",
"emailAddress": "[email protected]"
},
"progress": {
"identity": {
"status": "passed",
"completedAt": "2025-10-21T10:55:00Z"
},
"ais": {
"status": "passed",
"completedAt": "2025-10-21T10:58:00Z",
"accountsVerified": 2
}
}
}
}
Onboard Session Failed Event
{
"event": "onboard.session.failed",
"timestamp": "2025-10-21T11:00:00Z",
"data": {
"sessionId": "s1234567-89ab-cdef-0123-456789abcdef",
"customerId": "c1234567-89ab-cdef-0123-456789abcdef",
"status": "failed",
"customer": {
"id": "c1234567-89ab-cdef-0123-456789abcdef",
"firstName": "John",
"lastName": "Doe"
},
"progress": {
"identity": {
"status": "failed",
"completedAt": "2025-10-21T10:55:00Z",
"reason": "Document verification failed"
}
}
}
}
Handling Webhooks
Implementation Requirements
Your webhook endpoint must:
- โ Accept HTTP POST requests
- โ Return HTTP 200 status within 10 seconds
- โ Be publicly accessible (not localhost or private network)
- โ Use HTTPS in production (HTTP allowed for testing only)
- โ Process events asynchronously to avoid timeouts
Example Implementation (Node.js/Express)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/trustist', async (req, res) => {
// Immediately acknowledge receipt
res.status(200).send('OK');
// Process asynchronously
processWebhookAsync(req.body).catch(err => {
console.error('Webhook processing failed:', err);
});
});
async function processWebhookAsync(payload) {
const { event, data } = payload;
console.log(`Processing webhook: ${event}`);
switch (event) {
case 'onboard.session.completed':
await handleSessionCompleted(data);
break;
case 'onboard.session.failed':
await handleSessionFailed(data);
break;
default:
console.warn(`Unknown event type: ${event}`);
}
}
async function handleSessionCompleted(data) {
const { sessionId, customerId, progress } = data;
// Update your database
await db.customers.update(customerId, {
verificationStatus: 'verified',
verifiedAt: new Date(),
sessionId: sessionId
});
// Send notification email
if (progress.identity.status === 'passed') {
await sendWelcomeEmail(customerId);
}
// Trigger next steps in your workflow
await startAccountProvisioning(customerId);
}
async function handleSessionFailed(data) {
const { sessionId, customerId, progress } = data;
await db.customers.update(customerId, {
verificationStatus: 'failed',
failureReason: progress.identity.reason
});
await sendVerificationFailedEmail(customerId);
}
app.listen(3000);
Example Implementation (C# ASP.NET Core)
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("webhooks/trustist")]
public class TrustistWebhookController : ControllerBase
{
private readonly ILogger _logger;
private readonly IBackgroundTaskQueue _taskQueue;
public TrustistWebhookController(
ILogger logger,
IBackgroundTaskQueue taskQueue)
{
_logger = logger;
_taskQueue = taskQueue;
}
[HttpPost]
public IActionResult HandleWebhook([FromBody] WebhookPayload payload)
{
// Queue for background processing
_taskQueue.QueueBackgroundWorkItem(async token =>
{
await ProcessWebhookAsync(payload, token);
});
// Immediately return success
return Ok();
}
private async Task ProcessWebhookAsync(
WebhookPayload payload,
CancellationToken ct)
{
_logger.LogInformation("Processing webhook: {Event}", payload.Event);
switch (payload.Event)
{
case "onboard.session.completed":
await HandleSessionCompletedAsync(payload.Data, ct);
break;
case "onboard.session.failed":
await HandleSessionFailedAsync(payload.Data, ct);
break;
default:
_logger.LogWarning("Unknown event type: {Event}", payload.Event);
break;
}
}
}
Testing Webhooks
Local Development
For local testing, use a tunneling service to expose your localhost:
Using ngrok
# Install ngrok
npm install -g ngrok
# Start your local server (e.g., port 3000)
node server.js
# Create tunnel in another terminal
ngrok http 3000
# Use the ngrok HTTPS URL for your webhook
# Example: https://abc123.ngrok.io/webhooks/trustist
Using Hookdeck (recommended for testing)
Hookdeck provides webhook testing, inspection, and retry capabilities:
- Inspect webhook payloads in real-time
- Manually retry failed webhooks
- Test error scenarios
- View webhook logs and history
Testing Without a Server
Use webhook.site to get a temporary URL and inspect payloads:
- Visit webhook.site
- Copy your unique URL
- Register it as a webhook in Trustist
- Create a test identity verification session
- Complete it and view the payload on webhook.site
Managing Webhooks
List All Webhooks
/v1/onboard/webhooks
Response:
[
{
"id": "w1234567-89ab-cdef-0123-456789abcdef",
"url": "https://yourapp.com/webhooks/trustist",
"name": "Production Webhook",
"events": ["onboard.session.completed"],
"isActive": true,
"created": "2025-10-21T10:00:00Z",
"consecutiveFailures": 0
}
]
Delete a Webhook
/v1/onboard/webhooks/{webhookId}
Soft deletes the webhook by setting isActive = false.
Troubleshooting
Common Issues
Webhook Not Receiving Events
Check:
- Webhook URL is publicly accessible
- Firewall allows incoming connections
- SSL certificate is valid (for HTTPS)
- Webhook is active (
isActive: true) - Events array includes the event type
Webhook Disabled After Failures
If your webhook has 5+ consecutive failures, it will be automatically disabled. Common causes:
- Endpoint returns non-200 status code
- Response timeout (>10 seconds)
- Network connectivity issues
- Server errors (500, 502, 503)
Solution: Fix the issue, then delete and re-create the webhook to reactivate it.
Duplicate Events
In rare cases, you may receive the same event multiple times due to retries. Your webhook handler should be idempotent:
async function handleSessionCompleted(data) {
const { sessionId } = data;
// Check if already processed
const existing = await db.processedWebhooks.findOne({ sessionId });
if (existing) {
console.log('Event already processed, skipping');
return;
}
// Process the event
await updateCustomer(data.customerId);
// Mark as processed
await db.processedWebhooks.create({ sessionId, processedAt: new Date() });
}
Security Best Practices
- โ Use HTTPS for all webhook URLs in production
- โ Validate the webhook payload structure before processing
- โ Implement rate limiting on your webhook endpoint
- โ Log all webhook receipts for audit purposes
- โ Monitor for suspicious patterns or abuse
- โ Don't use webhooks for time-sensitive operations with strict SLAs
- โ Don't block the HTTP response while processing heavy operations
Next Steps
- Set up sandbox testing to test webhooks safely
- View API Reference for webhook endpoint details
- Return to Quick Start for complete integration workflow