Skip to main content

Webhook Payloads

When you create a verification link with a webhook_url, BotShield will POST a JSON event to that URL as soon as the verification state changes. This is the recommended way to receive results — polling is supported but adds latency and cost.

Event Types

Three events can fire per verification request:
EventWhen it fires
verification.successUser completed the biometric challenge successfully
verification.failedUser explicitly denied / cancelled, or the flow errored out
verification.expiredThe verification link TTL lapsed without a response
Exactly one event fires per verification request. Once an event has been delivered, the request is terminal — no further events will fire for that request_id.

Success Payload

{
  "event": "verification.success",
  "event_id": "evt_9d2f8b1c...",
  "request_id": "req_xyz789...",
  "organization_id": "org_abc123",
  "botshield_user_id": "728bc0c7-fb96-4ac9-b0e7-fa560374a079",
  "auth_mode": "private",
  "verified_at": "2026-04-14T12:00:00Z",
  "user_email": "[email protected]",
  "verification_token": "eyJhbGc...",
  "signal_strength": "strong",
  "metadata": {
    "order_id": "checkout-12345"
  }
}

Fields

FieldTypeDescription
eventstringAlways "verification.success"
event_idstringUnique event ID for idempotency. Safe to dedupe on.
request_idstringThe verification request ID you received when creating the link
organization_idstringYour organization ID
botshield_user_iduuidPseudonymous BotShield user ID. Stable for this user across verifications on this account.
auth_modeenum"linked-account" (user chose to associate with your platform) or "private" (anonymous)
verified_atstringISO 8601 timestamp of the biometric event
user_emailstring or nullUser email (only present when auth_mode === "linked-account")
verification_tokenstringSigned JWT receipt. Validate with sdk/verify-token.
signal_strengthenumV1.2 tier: newborn / growing / strong / trusted. See Signal Strength.
metadataobject or nullWhatever metadata object you included when creating the verification link

Failed Payload

{
  "event": "verification.failed",
  "event_id": "evt_...",
  "request_id": "req_xyz789...",
  "organization_id": "org_abc123",
  "reason": "user_denied",
  "failed_at": "2026-04-14T12:00:12Z",
  "metadata": {
    "order_id": "checkout-12345"
  }
}
reason is one of: user_denied, device_lock_required, internal_error, platform_declined.

Expired Payload

{
  "event": "verification.expired",
  "event_id": "evt_...",
  "request_id": "req_xyz789...",
  "organization_id": "org_abc123",
  "expired_at": "2026-04-14T12:10:00Z",
  "metadata": {
    "order_id": "checkout-12345"
  }
}
The default verification link TTL is 10 minutes. If the user does not complete the flow in that window, verification.expired fires.

Validating the Webhook

  1. Verify the signature using the signed verification_token — call sdk/verify-token to confirm the token was issued by BotShield and has not been tampered with.
  2. Check event_id for idempotency — BotShield may retry on delivery failure. Dedupe on event_id to avoid double-processing.
  3. Apply your policy based on signal_strength — the tier tells you how confident BotShield is. Your platform decides what tier gates which actions. See the Signal Strength guide.
app.post('/api/botshield-webhook', async (req, res) => {
  const { event, event_id, verification_token, signal_strength, request_id } = req.body;

  // Deduplicate
  if (await seenEvent(event_id)) return res.status(200).end();
  await recordEvent(event_id);

  if (event === 'verification.success') {
    // Validate the token signature + extract claims
    const result = await botshield.sdk.verifyToken({ token: verification_token });
    if (!result.data.valid) return res.status(400).end();

    // signal_strength is also in result.data.claims.signal_strength
    const tier = result.data.claims.signal_strength;
    await completeCheckout(request_id, tier);
  }

  res.status(200).end();
});

Delivery and Retries

  • BotShield sends webhooks over HTTPS with Content-Type: application/json.
  • If your endpoint returns a non-2xx status, BotShield retries with exponential backoff up to 3 attempts over 5 minutes.
  • A 200 OK response (empty body is fine) acknowledges receipt.
  • Webhooks that fail all retries are logged; you can replay them from the Partner Dashboard.