Webhooks
Webhooks notify your application in real-time when events occur in TrustGate, such as verification completions, screening hits, or case updates.
How Webhooks Work
┌─────────────────────────────────────────────────────────┐
│ TRUSTGATE │
│ │
│ Event occurs (e.g., verification completed) │
└─────────────────────────┬───────────────────────────────┘
│
│ HTTP POST
v
┌─────────────────────────────────────────────────────────┐
│ YOUR ENDPOINT │
│ │
│ https://your-app.com/webhooks/trustgate │
└─────────────────────────┬───────────────────────────────┘
│
│ Process event
v
┌─────────────────────────────────────────────────────────┐
│ YOUR APPLICATION │
│ │
│ Update database, notify user, trigger workflow │
└─────────────────────────────────────────────────────────┘
Creating Webhooks
Via API
curl -X POST https://api.bytrustgate.com/v1/webhooks \
-H "Authorization: Bearer tg_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/trustgate",
"events": [
"applicant.verified",
"applicant.rejected",
"screening.hit",
"case.created"
],
"description": "Production webhook"
}'
Response
{
"id": "webhook_abc123",
"url": "https://your-app.com/webhooks/trustgate",
"events": ["applicant.verified", "applicant.rejected", "screening.hit", "case.created"],
"secret": "whsec_your_webhook_secret_here",
"status": "active",
"created_at": "2025-01-20T14:00:00Z"
}
Important: Store the secret securely - it's used to verify webhook signatures.
Webhook Payload
Payload Structure
{
"id": "evt_123456",
"type": "applicant.verified",
"created_at": "2025-01-20T14:30:00Z",
"data": {
"applicant_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "verified",
"verification_level": "standard",
"checks_completed": ["document", "biometric", "screening"]
}
}
Headers
| Header | Description |
|---|---|
X-TrustGate-Signature | HMAC signature for verification |
X-TrustGate-Event | Event type |
X-TrustGate-Delivery | Unique delivery ID |
X-TrustGate-Timestamp | Unix timestamp |
Verifying Signatures
Always verify webhook signatures to ensure authenticity:
Node.js
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const timestamp = signature.split(',')[0].split('=')[1];
const receivedSignature = signature.split(',')[1].split('=')[1];
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(receivedSignature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/trustgate', (req, res) => {
const signature = req.headers['x-trustgate-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const event = req.body;
handleEvent(event);
res.status(200).send('OK');
});
Python
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
parts = dict(part.split('=') for part in signature.split(','))
timestamp = parts['t']
received_signature = parts['v1']
signed_payload = f"{timestamp}.{payload}"
expected_signature = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(received_signature, expected_signature)
@app.route('/webhooks/trustgate', methods=['POST'])
def webhook():
signature = request.headers.get('X-TrustGate-Signature')
payload = request.get_data(as_text=True)
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
handle_event(event)
return 'OK', 200
Handling Events
Event Handler Example
function handleEvent(event) {
switch (event.type) {
case 'applicant.verified':
handleVerified(event.data);
break;
case 'applicant.rejected':
handleRejected(event.data);
break;
case 'screening.hit':
handleScreeningHit(event.data);
break;
case 'case.created':
handleCaseCreated(event.data);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
}
function handleVerified(data) {
// Update user status in your database
await db.users.update({
where: { applicantId: data.applicant_id },
data: { kycStatus: 'verified' }
});
// Send confirmation email
await sendEmail(data.applicant_id, 'verification_complete');
// Trigger next onboarding step
await activateAccount(data.applicant_id);
}
Retry Policy
Automatic Retries
Failed deliveries are retried automatically:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 6 hours |
| 7 | 24 hours |
Success Criteria
A webhook is considered successful when your endpoint returns:
- HTTP status code
2xx - Within 30 seconds
Failure Handling
{
"webhook_id": "webhook_abc123",
"delivery_id": "del_xyz789",
"status": "failed",
"attempts": 3,
"last_error": "Connection timeout",
"next_retry_at": "2025-01-20T15:30:00Z"
}
Managing Webhooks
List Webhooks
curl -X GET "https://api.bytrustgate.com/v1/webhooks" \
-H "Authorization: Bearer tg_live_xxx"
Update Webhook
curl -X PATCH "https://api.bytrustgate.com/v1/webhooks/webhook_abc123" \
-H "Authorization: Bearer tg_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"events": ["applicant.verified", "applicant.rejected"],
"status": "active"
}'
Delete Webhook
curl -X DELETE "https://api.bytrustgate.com/v1/webhooks/webhook_abc123" \
-H "Authorization: Bearer tg_live_xxx"
Rotate Secret
curl -X POST "https://api.bytrustgate.com/v1/webhooks/webhook_abc123/rotate-secret" \
-H "Authorization: Bearer tg_live_xxx"
Testing Webhooks
Send Test Event
curl -X POST "https://api.bytrustgate.com/v1/webhooks/webhook_abc123/test" \
-H "Authorization: Bearer tg_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"event_type": "applicant.verified"
}'
View Delivery History
curl -X GET "https://api.bytrustgate.com/v1/webhooks/webhook_abc123/deliveries" \
-H "Authorization: Bearer tg_live_xxx"
Response
{
"deliveries": [
{
"id": "del_001",
"event_type": "applicant.verified",
"status": "delivered",
"response_code": 200,
"response_time_ms": 145,
"delivered_at": "2025-01-20T14:30:00Z"
},
{
"id": "del_002",
"event_type": "screening.hit",
"status": "failed",
"error": "Connection refused",
"attempts": 3,
"last_attempt_at": "2025-01-20T14:35:00Z"
}
]
}
Replay Failed Delivery
curl -X POST "https://api.bytrustgate.com/v1/webhooks/deliveries/del_002/retry" \
-H "Authorization: Bearer tg_live_xxx"
Best Practices
Endpoint Design
- Return 200 quickly - Process asynchronously if needed
- Handle duplicates - Events may be delivered more than once
- Verify signatures - Always validate webhook authenticity
- Use HTTPS - Required for production webhooks
- Log deliveries - Track received webhooks for debugging
Idempotency
Handle duplicate deliveries gracefully:
app.post('/webhooks/trustgate', async (req, res) => {
const eventId = req.body.id;
// Check if already processed
const existing = await db.webhookEvents.findUnique({
where: { eventId }
});
if (existing) {
return res.status(200).send('Already processed');
}
// Process and record
await processEvent(req.body);
await db.webhookEvents.create({
data: { eventId, processedAt: new Date() }
});
res.status(200).send('OK');
});
Next Steps
- Webhook Events - Complete event reference
- SDK Integration - Client SDK guide
- API Keys - Key management