Skip to main content
Webhooks give your backend a signed event at every step of a verification — from the moment it’s initialized to the moment it completes, including where people drop off. Use them to track funnel conversion, react to completion server-side, and tie verification activity back to your own analytics with external_tracker.

Register an endpoint

Add an HTTPS endpoint in the dashboard under Integration → Webhooks. We POST each event to that URL and show your signing secret once at registration — save it to verify signatures.

The event payload

Each event is a JSON body:
{
  "stage": "verification_completed",
  "step": 6,
  "occurred_at": "2026-06-09T17:04:01.123456+00:00",
  "verification_id": "ver_abc123",
  "external_id": "user_123",
  "external_tracker": "signup-campaign",
  "trust_score": 0.92
}
stage
string
The step that just happened (see the list below). Also sent as the X-VerifyYou-Event header.
step
number | null
The ordinal position of this stage in the happy path, when it has one.
occurred_at
string
ISO-8601 timestamp of the event.
verification_id
string
The verification this event belongs to.
external_id
string | null
The external_id linked at initialize, if any.
external_tracker
string | null
The non-PII label you passed at initialize — correlate funnel events with your analytics.
trust_score
number | null
The running trust score, when available.

Stages

StageMeaning
verification_initializedSession created.
verification_startedUser opened the flow.
account_createdAn account was created for the user.
consent_grantedUser accepted consent.
liveness_startedCamera / liveness step began.
liveness_passedLiveness succeeded.
liveness_failedLiveness failed.
liveness_cancelledUser abandoned the liveness step.
face_collisionUniqueness check matched an existing human.
verification_completedFlow finished.
The drop-off stages (liveness_failed, liveness_cancelled, face_collision) are where you’ll see funnel loss.

Verifying the signature

Every request carries two headers:
  • X-VerifyYou-Signaturesha256=<hmac>, an HMAC-SHA256 of the raw request body (hex), keyed with your signing secret.
  • X-VerifyYou-Event — the stage, for quick routing.
Compute the HMAC over the raw bytes and compare in constant time:
Node
import crypto from "node:crypto";

function verify(rawBody: Buffer, signatureHeader: string, secret: string): boolean {
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return (
    signatureHeader.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected))
  );
}
Verify against the raw body, before any JSON parsing or re-serialization — reformatting the bytes changes the signature.

Testing locally

Expose your local server with a tunnel, register the public URL, and use Send test event in the dashboard to deliver a signed sample.
cloudflared tunnel --url http://localhost:3000
# or: ngrok http 3000
Aggregate funnel and drop-off analytics via a Stats API are on the roadmap. Today, webhooks are the source of truth for per-step flow tracking.