Sign inGet started

Verify events

A verification produces events for its session and each delivery attempt. The session starts when Bird creates the verification and converts when the recipient enters the correct code. Each passcode send creates an attempt on one channel, which can be delivered or undelivered. Resends and channel failover add attempts to 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
verify.verification.failedSessionEvery planned channel reported that its send would not arrive, so no passcode reached the recipient
A verification that does not convert never emits verify.verification.verified, and its status does not tell you why on its own. failed is shared: a verification lands there both when too many incorrect passcodes were submitted, with reason attempts_exhausted, and when no planned channel could deliver one, with reason undeliverable. Only the second emits verify.verification.failed, and that event always carries reason undeliverable, so the event is what separates the two where the status cannot. A validity window that elapses resolves to expired. Neither expired nor an attempts-exhausted failed emits an event of its own. A fallback channel creates its own verify.attempt.sent, so one verification can have multiple 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 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.
Codebeispiel
{
  "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 when POST /v1/verify/verifications/check confirms the correct code. Adds status: "verified", channel (whichever channel delivered the submitted code, or null when the verification resolved without attributing a channel), and verified_at.
Codebeispiel
{
  "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 channel failover creates another attempt against the same verification_id, with its own delivery sequence. No event carries an attempt identifier, and webhook-id will not group them: it identifies one delivery of one event, so the sent and the delivered for a single attempt carry different values. Pair them on verification_id, channel and address in timestamp order. A resend on the same channel is the case that defeats this, since its events differ only by timestamp.

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.
Codebeispiel
{
  "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": "29999",
    "sent_at": "2026-07-23T14:45:59Z",
    "metadata": { "user_id": "usr_4821" }
  }
}

verify.attempt.delivered

Fires when the channel confirms that the passcode reached the recipient. Adds channel, address, carrier, mcc_mnc (the handling network and its mobile country/network code), and delivered_at. The carrier and mcc_mnc fields are always null for email, WhatsApp, and Telegram. This event omits from; read it from verify.attempt.sent for the same attempt.
Codebeispiel
{
  "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 deliver the passcode. Adds channel, address, reason (an open enum including carrier_rejected, hard_bounce, soft_bounce, undelivered, channel_unavailable, and channel_restricted), error (display-only detail, or null), and failed_at. Like verify.attempt.delivered, this event omits from.
Codebeispiel
{
  "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 does not end the verification. Bird advances to the next channel in the delivery plan, which gets its own verify.attempt.sent. A channel that fails before sending emits verify.attempt.undelivered with reason: "channel_unavailable" and advances in the same way, as does one that does not carry passcodes to the recipient's country, with reason: "channel_restricted" (see Country configuration). That attempt has no verify.attempt.sent or later delivery report. If every channel fails, Bird emits one verify.attempt.undelivered per attempt.
Delivery reports are indicative rather than guaranteed. Carriers and mailbox providers vary in what they confirm and how quickly. In some markets, attempt events arrive minutes later or do not distinguish delivery from acceptance. Treat verify.verification.verified as the definitive signal that a recipient received and used their code.

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