Webhooks Guide
Webhooks deliver real-time event notifications to your server when things happen in Verifa — sessions complete, screening finds matches, identities update, and more. This guide covers setup, security, event filtering, and best practices.
Quick setup
1. Create a webhook endpoint
Store the secret securely — you need it to verify webhook signatures. The
whsec_* value is shown once at creation and again only when you rotate
it. You cannot retrieve it later. Each endpoint has its own per-endpoint
secret, so rotating one endpoint never affects another.
Per-endpoint signing secrets are mandatory for endpoints created on or after
2026-04-17. Endpoints created before that date may still fall back to the
deprecated org-level webhook secret; rotate those endpoints to mint a
dedicated whsec_* and stop relying on the org-wide value.
2. Handle events
The X-Verifa-Signature header uses the Stripe-style format
t=<unix_ts>,v1=<hex_hmac>. The HMAC-SHA256 covers f"{t}.{raw_body}" and
receivers should reject any delivery whose t= is more than 5 minutes from
current time (replay protection). See the
Tutorial: Receiving Webhooks & Validating Signatures
for full code samples and a reference recipe.
JavaScript
Python
3. Test it
Send a test event to verify your endpoint is working:
Event catalog
Session events
Screening events
Identity events
Consortium events
Case events
Document events
Quality assurance events
Event filtering
Subscribe to specific events or use "*" to receive all events:
Payload format
All webhook payloads use the same envelope: event, created_at, and
idempotency_key sit at the top level, and every event-specific field is
nested under data.
Route on the top-level event, then read resource fields from data (e.g.
body.data.session_id). The data contents vary by event type — session
events include session_id, external_ref, and status; identity events
include identity_id and the changed fields. See
Handling webhook events for the full
per-event field reference.
Signature verification
Every webhook includes an X-Verifa-Signature header in the Stripe-style
format t=<unix_ts>,v1=<hex_hmac>:
t— Unix timestamp the signature was generated atv1— hex-encoded HMAC-SHA256 off"{t}.{raw_request_body}", signed with your endpoint’swhsec_*secret
Always verify signatures to prevent spoofed events. Use the raw request
body bytes (not parsed JSON) for verification, use a constant-time comparison
function for the v1 value, and reject deliveries whose t= is more than
5 minutes from your current clock to prevent replay attacks. See the
Tutorial: Receiving Webhooks & Validating Signatures
for the full reference recipe and language-specific examples.
The verification helper at src/core/security.py:verify_webhook_signature
also accepts the legacy bare-hex HMAC over the raw body for in-flight
deliveries during the deprecation window. New outbound deliveries always use
the timestamped t=,v1= format, so build new receivers against that format.
Replay protection
Receivers should layer the following defenses:
- Verify the signature using the
t=,v1=recipe above — reject anything that doesn’t validate. - Reject stale timestamps. If
abs(now - t) > 300, drop the delivery even if the HMAC checks out. This is the same 5-minute window enforced by every Verifa client. - Dedupe on
idempotency_key. Verifa retries failed deliveries, and the same event may arrive more than once with the sameidempotency_keyin the payload body. Maintain a short-lived cache (Redis, DB row) keyed onidempotency_keyto skip duplicates rather than hashing payloads.
Retry behavior
If your endpoint returns a non-2xx status or doesn’t respond within 30 seconds, Verifa retries with exponential backoff:
After 5 failed retries, the delivery is marked as failed.
Circuit breaker
If an endpoint accumulates 10 consecutive failed deliveries (all retries exhausted), Verifa automatically disables the endpoint and sends an email notification to your organization’s admins. Re-enable it in the dashboard or via API after fixing the issue.
Troubleshooting delivery failures
Deliveries are blocked by a firewall, WAF, or bot protection
This is the single most common reason a brand-new webhook endpoint never
receives events. Verifa delivers webhooks as server-to-server POST requests —
there is no browser, no JavaScript engine, and no human behind them. Bot-protection
products are tuned to flag exactly that traffic shape, so they often challenge or
block the request before it ever reaches your application.
The tell-tale sign is a delivery that fails with HTTP 403 and a response
body containing an interstitial challenge page (for example, Cloudflare’s
"Just a moment..." page) rather than your own API’s response. Check the
delivery’s error in Developers → Webhooks → delivery history.
A curl from your laptop or a browser test will usually succeed even when
production deliveries fail — you look “human” enough to pass the challenge, or
you solve it interactively. Because of this, the problem is typically invisible
until your first real delivery.
Common culprits and fixes:
- Cloudflare — Bot Fight Mode, Super Bot Fight Mode, Managed Challenge, or
Browser Integrity Check. Add a WAF custom rule that skips these for your
webhook path (e.g.
URI Path equals /webhooks/verifa), or set a configuration-rule exception for that route. - AWS WAF / Akamai / Imperva / Cloudfront — add an allow rule for the webhook
path, or for requests carrying the
X-Verifa-Signatureheader. - Self-managed firewalls / IP allowlists — allow inbound traffic to your webhook path from Verifa’s egress.
You can scope the exception narrowly by matching on either of these, both present on every delivery:
- the
X-Verifa-Signatureheader — only HMAC-signed Verifa traffic bypasses the challenge; or - our
User-Agent, which always starts withVerifa-Webhooks/(e.g.Verifa-Webhooks/1.0 (+https://docs.withverifa.com/webhooks)).
For example, in Cloudflare a custom rule of Skip → when User-Agent starts
with Verifa-Webhooks/ is enough to let deliveries through.
Allowlisting your webhook path past a WAF does not weaken your security,
provided you verify the signature. Every delivery is HMAC-signed with your
endpoint’s whsec_* secret, so your handler can still reject any request that
isn’t genuinely from Verifa. See Signature verification.
Never process unsigned or unverified requests, regardless of where they came from.
Other things to check
- TLS errors — confirm your endpoint serves a valid, non-expired certificate for the exact hostname in the URL, and that it presents the full chain. Verifa validates certificates and will not deliver to a host that fails the handshake.
- Timeouts — your handler must return a response within 30 seconds. Return
200immediately and process asynchronously (see Best practices). - Wrong status code — only
2xxcounts as success. A3xxredirect is treated as a failure; point the endpoint URL directly at its final location. - Reachability — the URL must be publicly resolvable.
localhost, private IP ranges, and internal-only hostnames are rejected. Use a tunnel (e.g. ngrok) for local development.
Managing endpoints
Rotate the signing secret
Returns the new whsec_* secret in the response body. This is the only other
time the secret is shown — store it immediately. The previous secret is
invalidated as soon as rotation completes, so deploy the new value to your
server before or immediately after rotating. Rotating one endpoint never
affects another endpoint’s secret.
Clone an endpoint
Duplicate an endpoint with a new secret (useful for migration):
View delivery history
Retry a failed delivery
Key inflection
Control the casing of payload keys per endpoint:
Options: snake (default), camel, kebab.
Best practices
-
Respond quickly. Return
200immediately and process the event asynchronously. If your handler takes too long, the delivery may time out and trigger unnecessary retries. -
Handle duplicates. Use the
idempotency_keyfield on the payload body to deduplicate — the same key arrives on every retry of the same delivery. In rare cases (network issues, retries), you may receive the same event more than once. -
Verify signatures and timestamps. Parse
t=andv1=from theX-Verifa-Signatureheader, recompute the HMAC overf"{t}.{raw_body}", and reject any delivery whoset=is more than 5 minutes from now. Never process unverified events. -
Exempt from CSRF. If your framework has CSRF protection, exempt your webhook endpoint path.
-
Use specific events. Subscribe only to the events you need rather than
"*". This reduces noise and makes your handler simpler. -
Monitor delivery health. Check the delivery history in the dashboard regularly. If you see frequent failures, investigate timeouts or errors on your endpoint.
Related
- Webhooks — Quick reference for event types and payload format
- Sessions — Session events and lifecycle
- Screening & Reports — Screening event details
- Errors — Error codes returned by webhook API endpoints