Documentation
Sign inGet started

Verify events

A verification produces events on two axes. One is the session: it's created, and it eventually converts when the recipient enters the right code. The other is the delivery of each passcode Bird sends toward that recipient: an attempt goes out on a channel, and it either reaches the recipient or it doesn't. A verification that resends a code, or that fails over from SMS to WhatsApp, produces more than one delivery attempt against the same session.
EventAxisFires when
verify.verification.createdSessionA verification is created and the first passcode is queued for send
verify.attempt.sentDeliveryA passcode has been handed to a channel for delivery
verify.attempt.deliveredDeliveryThe channel confirmed the passcode reached the recipient
verify.attempt.undeliveredDeliveryThe channel could not get the passcode to the recipient
verify.verification.verifiedSessionThe recipient submitted the correct code before the verification expired
Not every verification reaches verify.verification.verified: one that expires or exhausts its check attempts stops without ever emitting it, the same way a verification's own status can resolve to failed or expired instead of verified. On the delivery axis, an attempt on a fallback channel produces its own verify.attempt.sent, so a single verification with a channel failover can carry two full attempt sequences.
The event type list is open: new types may be added over time, so treat an unrecognized value as a future event rather than an error.

The event envelope

Events arrive at your webhook endpoint in the Standard Webhooks nested envelope described in the Webhooks guide: a type, a timestamp, and a type-specific data object. The event's identity is not in the body: it rides in the webhook-id HTTP header, which is stable across retries of the same delivery and is your deduplication key.
Every event's data carries this identity base:
  • verification_id: the verification this event belongs to, matching the id from POST /v1/verify/verifications
  • workspace_id: the workspace that created the verification
  • to: the recipient identity of the verification, an object with email_address and/or phone_number matching what the create request supplied. A single passcode attempt reports the one address it went to in its own address field
  • metadata: the free-form object from the create request, echoed unchanged, or null when the request carried none

Session events

verify.verification.created

Fires as soon as a verification is created and its first passcode is queued. Adds channel (the channel the first attempt is going out on), status: "pending", and created_at.
Ejemplo de código
{
  "type": "verify.verification.created",
  "timestamp": "2026-07-23T14:45:58Z",
  "data": {
    "verification_id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "channel": "sms",
    "to": { "phone_number": "+14155550100" },
    "status": "pending",
    "created_at": "2026-07-23T14:45:58Z",
    "metadata": { "user_id": "usr_4821" }
  }
}

verify.verification.verified

Fires the moment POST /v1/verify/verifications/check confirms the correct code. Adds status: "verified", channel (whichever channel delivered the code the recipient actually entered, or null when the verification resolved without attributing a channel), and verified_at.
Ejemplo de código
{
  "type": "verify.verification.verified",
  "timestamp": "2026-07-23T14:46:38Z",
  "data": {
    "verification_id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "to": { "phone_number": "+14155550100" },
    "status": "verified",
    "channel": "sms",
    "verified_at": "2026-07-23T14:46:38Z",
    "metadata": { "user_id": "usr_4821" }
  }
}

Delivery events

Every passcode Bird sends is one attempt. A resend or a channel failover creates a new attempt against the same verification_id, each with its own delivery sequence below; the webhook-id header keeps each attempt's events distinct even when their payloads look alike.

verify.attempt.sent

Fires once Bird has handed the passcode to the channel. Adds channel, address (the single address this attempt was sent to, an E.164 phone number or an email address), from (the sending address or number, null when the channel exposes no sender), and sent_at.
Ejemplo de código
{
  "type": "verify.attempt.sent",
  "timestamp": "2026-07-23T14:45:59Z",
  "data": {
    "verification_id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "to": { "phone_number": "+14155550100" },
    "channel": "sms",
    "address": "+14155550100",
    "from": "Authifly",
    "sent_at": "2026-07-23T14:45:59Z",
    "metadata": { "user_id": "usr_4821" }
  }
}

verify.attempt.delivered

Fires when the channel confirms the passcode reached the recipient. Adds channel, address, carrier, and mcc_mnc (the handling network and its mobile country/network code, always null for email and WhatsApp), and delivered_at. There is no from on this event; the sender rode on verify.attempt.sent for the same attempt.
Ejemplo de código
{
  "type": "verify.attempt.delivered",
  "timestamp": "2026-07-23T14:46:03Z",
  "data": {
    "verification_id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "to": { "phone_number": "+14155550100" },
    "channel": "sms",
    "address": "+14155550100",
    "carrier": "Example Wireless",
    "mcc_mnc": "310260",
    "delivered_at": "2026-07-23T14:46:03Z",
    "metadata": { "user_id": "usr_4821" }
  }
}

verify.attempt.undelivered

Fires when the channel could not get the passcode to the recipient. Adds channel, address, reason (an open enum: carrier_rejected, hard_bounce, undelivered, channel_unavailable, with more added over time), error (a human-readable, display-only string describing what happened, null when the channel gave no detail), and failed_at. There is no from on this event, the same as verify.attempt.delivered.
Ejemplo de código
{
  "type": "verify.attempt.undelivered",
  "timestamp": "2026-07-23T14:46:04Z",
  "data": {
    "verification_id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
    "to": { "phone_number": "+14155550100" },
    "channel": "sms",
    "address": "+14155550100",
    "reason": "carrier_rejected",
    "error": "Carrier rejected the message before delivery",
    "failed_at": "2026-07-23T14:46:04Z",
    "metadata": { "user_id": "usr_4821" }
  }
}
An undelivered attempt on a recipient with more than one channel available doesn't end the verification: Bird advances to the next channel in the delivery plan and that attempt gets its own verify.attempt.sent. A channel that fails at send time, before the message ever leaves Bird, reports the same event with reason: "channel_unavailable" and advances the failover the same way: there is no verify.attempt.sent for that attempt, and no delivery report follows it. So a verification whose every channel fails still tells the whole story: one verify.attempt.undelivered per attempt, never silence after verify.verification.created.
Delivery reports are indicative, not a hard guarantee: carriers and mailbox providers vary in what they confirm and how quickly, so verify.attempt.delivered and verify.attempt.undelivered can arrive minutes apart from the send, or, for some carriers and markets, not distinguish delivery from acceptance at all. Treat verify.verification.verified as the definitive signal that a recipient received and used their code; the attempt events tell you where it went, not a guarantee it was read.

Webhooks

Subscribe an endpoint to any verify.* type from the Webhooks page in the dashboard or through the webhooks API. The Webhooks guide covers creating endpoints, verifying the Standard Webhooks signature, retries, and replaying missed deliveries.

Next steps

PageWhat it covers
Sending verificationsThe send and check calls, statuses, settings, and limits
Webhooks & eventsEndpoint setup, signature verification, retries, and replay
API reference: create a verificationSend-endpoint schema and error details