Verify 事件
一次验证会为其会话和每次投递尝试产生事件。会话在 Bird 创建验证时开始,当接收者输入正确的验证码时转化。每次验证码发送会在一个渠道上创建一次尝试,结果为已送达或未送达。重新发送和渠道故障转移会向同一会话添加尝试。
| 事件 | 维度 | 触发条件 |
|---|---|---|
| verify.verification.created | 会话 | 验证已创建,首条验证码已排队等待发送 |
| verify.attempt.sent | 投递 | 验证码已移交给渠道进行投递 |
| verify.attempt.delivered | 投递 | 渠道确认验证码已送达收件人 |
| verify.attempt.undelivered | 投递 | 渠道未能将验证码送达收件人 |
| verify.verification.verified | 会话 | 收件人在验证过期前提交了正确的验证码 |
| verify.verification.failed | 会话 | 投递计划以失败结束,表明未发送任何验证码 |
未转化的验证永远不会发出 verify.verification.verified,其状态本身也无法告诉你原因。failed 是共用的:提交了过多错误验证码时(带 reason attempts_exhausted),以及投递计划以失败结束表明未发送任何验证码时(带 reason undeliverable),验证都会进入该状态。只有第二种情况会发出 verify.verification.failed,且该事件始终携带 reason undeliverable,因此在状态无法区分时,事件是区分两者的依据。有效期耗尽后将解析为 expired。expired 和尝试次数耗尽的 failed 都不会发出自己的事件。回退渠道会创建自己的 verify.attempt.sent,因此一次验证可以有多个尝试序列。
事件类型列表是开放的:新类型可能随时增加,因此请将无法识别的值视为未来事件而非错误。
事件信封
事件通过 Standard Webhooks 嵌套信封到达你的 webhook 端点,具体结构参见 Webhooks 指南:一个 type、一个 timestamp 和一个特定类型的 data 对象。事件的标识不在请求体中:它位于 webhook-id HTTP 请求头中,该值在同一投递的重试之间保持不变,是你的去重键。
每个事件的 data 携带以下基本标识信息:
- verification_id:该事件所属的验证,与 POST /v1/verify/verifications 返回的 id 匹配
- workspace_id:创建该验证的工作区
- to:验证的接收者身份,包含 email 和/或 phone_number 的对象,与创建请求提供的内容一致。单次验证码尝试在自己的 address 字段中报告其发送到的地址
- metadata:创建请求中的自由格式对象,原样回传;如果请求未携带则为 null
会话事件
verify.verification.created
在验证被创建且首条验证码排队后立即触发。增加 channel(首次尝试使用的渠道)、status: "pending" 和 created_at。
代码示例
{
"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
当 POST /v1/verify/verifications/check 确认验证码正确时触发。增加 status: "verified"、channel(投递所提交验证码的渠道,或在验证未归因到渠道时为 null)和 verified_at。
代码示例
{
"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" }
}
}verify.verification.failed
当投递计划耗尽且记录的失败表明未发送任何验证码时触发。载荷中增加 status: "failed"、reason: "undeliverable"、channel(最后尝试的渠道,无归属渠道时为 null)、last_attempt_reason 和 failed_at。
channel_unavailable、channel_disabled、channel_restricted 和 not_billable 表示某次尝试未发送验证码。如果某次尝试可能已发送验证码,后续的退信、运营商拒绝或投递超时会使会话保持待定状态,且不会发出 verify.verification.failed。先前的验证码在过期前仍可完成验证。
last_attempt_reason 使用与 verify.attempt.undelivered 相同的失败原因。not_billable 失败表示发送无法扣费;请检查工作区余额以及目的地是否有可用定价。
投递事件
Bird 发送的每条验证码即为一次尝试。重新发送或渠道故障转移会在同一 verification_id 下创建新的尝试,拥有独立的投递序列。事件不携带尝试标识符,webhook-id 也不会将其分组:它标识的是一个事件的一次投递,因此同一次尝试的 sent 和 delivered 携带不同的值。按时间戳顺序通过 verification_id、channel 和 address 配对它们。在同一渠道上重新发送是无法通过此方式区分的情况,因为其事件仅在时间戳上不同。
verify.attempt.sent
当 Bird 将验证码移交给渠道后触发。增加 channel、address(此次尝试发送到的单一地址,E.164 格式电话号码或电子邮件地址)、from(发送地址或号码,渠道未公开发送方时为 null)和 sent_at。
代码示例
{
"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
当渠道确认验证码已送达收件人时触发。增加 channel、address、carrier、mcc_mnc(处理网络及其移动国家/网络代码)和 delivered_at。对于 email、WhatsApp 和 Telegram,carrier 和 mcc_mnc 字段始终为 null。此事件不包含 from;请从同一次尝试的 verify.attempt.sent 中读取。
代码示例
{
"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
当渠道无法投递验证码时触发。增加 channel、address、reason(开放枚举,包括 carrier_rejected、hard_bounce、soft_bounce、undelivered、channel_unavailable、channel_restricted、channel_disabled、delivery_timeout 和 not_billable)、error(仅用于显示的详情,或 null)和 failed_at。与 verify.attempt.delivered 一样,此事件不包含 from。
代码示例
{
"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" }
}
}在拥有多个可用渠道的收件人上,一次未送达的尝试不会结束验证。Bird 会推进到投递计划中的下一个渠道,该渠道获得自己的 verify.attempt.sent。在发送前失败的渠道会发出带有 reason: "channel_unavailable" 的 verify.attempt.undelivered 并以相同方式推进;不支持向收件人所在国家投递验证码的渠道也是如此,带有 reason: "channel_restricted"(参见国家配置)。该尝试没有 verify.attempt.sent 或后续投递报告。Bird 为每次失败的尝试发出 verify.attempt.undelivered。如果计划耗尽且记录的失败表明未发送任何验证码,它还会为该会话发出 verify.verification.failed。
投递报告是参考性的,不保证完整。运营商和邮箱服务商在确认内容和速度上各有不同。在某些市场,尝试事件会延迟数分钟到达,或无法区分已投递与已接受。将 verify.verification.verified 视为收件人已收到并使用验证码的最终信号。
Webhooks
在控制面板的 Webhooks 页面或通过 webhooks API 将端点订阅到任意 verify.* 类型。Webhooks 指南涵盖了创建端点、验证 Standard Webhooks 签名、重试以及重放失败的投递。
后续步骤
| 页面 | 涵盖内容 |
|---|---|
| 发送验证 | 发送和检查调用、状态、设置和限制 |
| Webhooks 与事件 | 端点设置、签名验证、重试和重放 |
| API 参考:创建验证 | 发送端点的 schema 和错误详情 |