Veriff Webhooks
Webhooks allow you to receive real-time notifications when verification processes complete. This is essential for handling asynchronous verifications and building responsive user experiences.
Overview
When a verification request is processed asynchronously, LoomAPI sends an HTTP POST request to your configured webhook URL with the results. This eliminates the need for polling and enables immediate user notifications.
Setup
1. Configure Webhook URL
Set up your webhook endpoint in the dashboard:
- Go to dashboard.loomapi.com
- Navigate to Webhooks section
- Add your webhook URL
- Configure secret for signature verification
2. Webhook URL Requirements
- HTTPS Only: Webhooks must use HTTPS (HTTP not supported)
- Public Access: URL must be accessible from the internet
- Fast Response: Respond within 5 seconds to avoid timeouts
- Idempotent: Handle duplicate deliveries gracefully
Webhook Payload
{
"event": "verification.completed",
"request_id": "req_1234567890abcdef",
"timestamp": "2024-01-15T14:30:00Z",
"verification": {
"status": "verified",
"verified_age": 25,
"confidence_score": 0.98,
"document_type": "passport",
"processing_time_ms": 2340
},
"metadata": {
"user_id": "user_123",
"session_id": "session_456"
}
}
Payload Fields
event: Event type (see Event Types)request_id: Original verification request IDtimestamp: ISO 8601 timestamp of the eventverification: Verification results (same as sync response)metadata: Custom metadata from your original request
Event Types
verification.completed
Sent when verification processing finishes successfully.
{
"event": "verification.completed",
"request_id": "req_1234567890abcdef",
"verification": {
"status": "verified",
"verified_age": 25,
"confidence_score": 0.98
}
}
verification.failed
Sent when verification fails due to processing errors.
{
"event": "verification.failed",
"request_id": "req_1234567890abcdef",
"error": {
"code": "DOCUMENT_QUALITY_LOW",
"message": "Document quality insufficient for processing"
}
}
verification.expired
Sent when a verification session expires before completion.
{
"event": "verification.expired",
"request_id": "req_1234567890abcdef",
"expired_at": "2024-01-15T15:00:00Z"
}
Signature Verification
All webhook requests include an X-Loom-Signature header for security verification.
Signature Format
X-Loom-Signature: t=1640995200,v1=abc123def456...
Where:
t: Unix timestampv1: HMAC-SHA256 signature
Verification Process
const crypto = require('crypto')
function verifyWebhookSignature(payload, signature, secret) {
// Extract timestamp and signature
const [timestampPart, signaturePart] = signature.split(',')
const timestamp = timestampPart.split('=')[1]
const expectedSignature = signaturePart.split('=')[1]
// Create signed payload
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`
// Calculate expected signature
const calculatedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload, 'utf8')
.digest('hex')
// Compare signatures
return crypto.timingSafeEqual(
Buffer.from(calculatedSignature, 'hex'),
Buffer.from(expectedSignature, 'hex')
)
}
import hmac
import hashlib
import json
from typing import Dict, Any
def verify_webhook_signature(payload: Dict[str, Any], signature: str, secret: str) -> bool:
# Extract timestamp and signature
timestamp_part, signature_part = signature.split(',')
timestamp = timestamp_part.split('=')[1]
expected_signature = signature_part.split('=')[1]
# Create signed payload
signed_payload = f"{timestamp}.{json.dumps(payload, separators=(',', ':'))}"
# Calculate expected signature
calculated_signature = hmac.new(
secret.encode('utf-8'),
signed_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Compare signatures using constant-time comparison
return hmac.compare_digest(calculated_signature, expected_signature)
Security Best Practices
- Always verify signatures before processing webhooks
- Use HTTPS for your webhook endpoint
- Store secrets securely (environment variables, not code)
- Implement timeouts to prevent hanging requests
- Log webhook attempts for debugging
Handling Webhooks
Basic Handler
app.post('/webhooks/loom', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-loom-signature']
const secret = process.env.LOOM_WEBHOOK_SECRET
// Verify signature
if (!verifyWebhookSignature(req.body, signature, secret)) {
console.error('Invalid webhook signature')
return res.status(401).send('Unauthorized')
}
// Process webhook
const { event, request_id, verification } = req.body
switch (event) {
case 'verification.completed':
handleVerificationCompleted(request_id, verification)
break
case 'verification.failed':
handleVerificationFailed(request_id, verification)
break
default:
console.log(`Unhandled event: ${event}`)
}
// Respond quickly
res.status(200).send('OK')
})
Idempotency
Handle duplicate webhooks gracefully:
const processedWebhooks = new Set()
function processWebhook(requestId, eventType, data) {
const webhookId = `${requestId}-${eventType}`
if (processedWebhooks.has(webhookId)) {
console.log(`Duplicate webhook ignored: ${webhookId}`)
return
}
// Process webhook
// ... your logic here ...
// Mark as processed
processedWebhooks.add(webhookId)
// Optional: Clean up old entries periodically
if (processedWebhooks.size > 10000) {
// Clear old entries (implement based on your needs)
}
}
Retry Policy
Automatic Retries
LoomAPI automatically retries failed webhook deliveries:
- Retry Schedule: 1min, 5min, 15min, 1hr, 6hr, 24hr
- Max Attempts: 7 attempts over 24 hours
- Backoff: Exponential backoff with jitter
Failure Handling
Webhook delivery fails if:
- HTTP status code ≥ 300
- Connection timeout (>5 seconds)
- DNS resolution failure
- SSL/TLS errors
Manual Retries
You can manually retry failed webhooks from the dashboard or trigger them via API:
curl -X POST https://api.loomapi.com/v1/webhooks/retry \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"request_id": "req_1234567890abcdef"}'
Testing Webhooks
Development Tools
Use tools like ngrok or localtunnel to expose local development servers:
# Using ngrok
ngrok http 3000
# Forwarding: https://abc123.ngrok.io -> http://localhost:3000
# Using localtunnel
npx localtunnel --port 3000
# Tunnel URL: https://random-name.loca.lt
Test Mode
Use test mode to trigger webhook events without real verifications:
curl -X POST https://api.loomapi.com/v1/webhooks/test \
-H "Authorization: Bearer your_test_key" \
-H "Content-Type: application/json" \
-d '{
"event": "verification.completed",
"test_data": {
"status": "verified",
"verified_age": 25
}
}'
Monitoring
Dashboard Monitoring
Monitor webhook delivery in your dashboard:
- Delivery success rates
- Response times
- Failure reasons
- Recent deliveries
Logging
Implement comprehensive logging:
function logWebhook(requestId, event, status, details = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
request_id: requestId,
event,
status, // 'received', 'processed', 'failed'
details,
user_agent: req.get('User-Agent'),
ip_address: req.ip
}
console.log(JSON.stringify(logEntry))
// Optional: Send to monitoring service
// monitoringService.log('webhook', logEntry)
}
Troubleshooting
Common Issues
- Signature verification fails: Check timestamp format and secret key
- Timeouts: Ensure your endpoint responds within 5 seconds
- Duplicate processing: Implement idempotency checks
- Missing webhooks: Check firewall rules and HTTPS configuration
Debug Mode
Enable debug logging for detailed webhook information:
curl -X PUT https://api.loomapi.com/v1/webhooks/debug \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"enabled": true}'
This provides additional headers with debugging information.
Best Practices
Security
- Always verify webhook signatures
- Use HTTPS endpoints only
- Implement rate limiting on your webhook endpoints
- Validate payload structure before processing
Reliability
- Implement idempotency to handle duplicates
- Respond quickly to webhook requests
- Monitor delivery success rates
- Have fallback mechanisms for critical processes
Performance
- Process webhooks asynchronously in your application
- Use queues for high-volume processing
- Monitor response times and optimize slow endpoints
- Implement circuit breakers for downstream failures
Support
Need help with webhooks?
- Documentation: Check our webhook guides
- Examples: See code examples
- Community: Join our developer forum
- Support: Email support@loomapi.com