Sign inGet Started

SMS 事件

每条消息都会经历一个生命周期,Bird 在每个步骤发出一个事件。本页是完整的事件词汇表;事件如何投递到你的端点(签名、重试、回放)请参阅 Webhooks 指南。
投递生命周期,以事件类型为路径:
  1. sms.accepted:Bird 已收到消息,正在准备将其交给运营商。
  2. sms.sent:Bird 已将消息交给运营商,正在等待投递回执。
  3. 一个终态事件:
    • sms.delivered:运营商确认消息已投递到终端。
    • sms.undelivered:运营商报告了临时未投递,例如终端不可用。
    • sms.failed:永久性失败导致投递中止。
    • sms.expired:运营商停止尝试,并将消息报告为已过期。
终态事件呈现运营商的投递回执,即 SMS 平台所称的投递报告(DLR)。
例外是 sms.rejected:消息被拒绝(由策略检查、无法完成的扣费或拒绝接收的运营商),而非尝试投递后丢失。在处理阶段被拒绝的消息仅携带 sms.rejected 作为唯一事件。
Bird 也接收回复。当订阅者向你的某个号码发送短信时,Bird 存储该消息并发出 sms.received,你无需轮询即可做出响应。载荷包含正文、分段明细、双方号码,以及运营商上报时的运营商信息。
Bird 根据该号码的关键词规则评估回复。受支持的停止关键词(如 STOP)会记录一条发送方-订阅者屏蔽,同时仍然发出 sms.received。
事件 type 是一个开放枚举:Bird 可能会随时间添加新的事件类型,因此将无法识别的 type 视为未来事件而非错误。匹配你需要处理的类型,忽略其余类型。

事件信封

事件以 Standard Webhooks 嵌套信封的形式到达你的 webhook 端点,详见 Webhooks 指南:三个字段 type、timestamp,以及一个特定于类型的 data 对象。事件的标识不在请求体中:它位于 webhook-id HTTP 请求头中,在同一投递的多次重试间保持稳定,是你的去重键。
字段说明
type本页列出的事件类型之一,例如 sms.delivered
timestamp事件发生的时间(RFC 3339);按此排序,切勿按到达顺序排序,因为投递不保证有序
data特定于事件的载荷
每个 SMS 事件的 data 包含 sms_id、workspace_id,以及 to 和 from 地址。它还会回显发送时的 tags 和 metadata,方便你路由和关联事件而无需额外查询。当发送未携带时,各字段值为 null。
同一对象携带 cost,即截至该事件时消息的费用,拆分为 transaction_amount 和 passthrough_amount,两者之和在 amount 中。在未产生计费的事件上其值为 null。由于投递不保证有序,请逐项合并 cost 而非整体替换:对于每个分项,保留 timestamp 最新的事件中的值。amount 仅汇总其自身载荷中的分项,因此应将其视为截至目前的费用而非最终总额。费用与计费介绍了每个分项的含义。
代码示例
{
  "type": "sms.delivered",
  "timestamp": "2026-06-10T14:30:00Z",
  "data": {
    "sms_id": "sms_01krdgeqcxet5s7t44vh8rt9mg",
    "workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
    "to": "+15551234567",
    "from": "+12025550188",
    "carrier": "Example Wireless",
    "mcc_mnc": "310260",
    "cost": {
      "amount": "0.00990",
      "currency_code": "USD",
      "transaction_amount": "0.00790",
      "passthrough_amount": "0.00200"
    },
    "tags": [{ "name": "campaign", "value": "spring-2026" }],
    "metadata": { "order_id": "ord_123" }
  }
}

生命周期事件

sms.accepted

在 Bird 接受发送并开始准备将其交给运营商时触发。载荷增加 segments,即在接受时统计的分段明细 Bird;其 count 是该发送的计费依据。

sms.sent

在 Bird 将消息交给运营商并等待投递回执时触发。载荷增加 carrier 和 mcc_mnc(处理网络及其移动国家/网络代码)。当运营商未上报时,各字段缺失而非为 null。要测量处理延迟,请将该事件的 timestamp 与 sms.accepted 的进行比较。

sms.delivered

运营商确认消息已到达终端。载荷增加 carrier 和 mcc_mnc,当回执未标识它们时缺失。

失败事件

每个失败事件的载荷增加一个 error 对象:一个跨 Bird 稳定的 code(例如 unreachable 或 blocked_by_carrier)、一个人类可读的 description、运营商提供时的原始 carrier_error_code,以及 occurred_at。

sms.undelivered

非永久性未投递:终端已关机或不可达。

sms.failed

永久性投递失败导致消息中止。

sms.rejected

消息在处理过程中被 Bird 的检查拒绝、扣费无法完成或运营商拒绝接收。拒绝会在投递尝试成功前中止消息。余额耗尽时以错误代码 insufficient_balance 终止于此,扣费无法完成的消息不会被计费。

sms.expired

运营商停止尝试投递,并将消息报告为已过期。过期来自运营商的投递回执:Bird 不设置自身的有效期窗口,也不运行任何使消息终止的计时器。error 描述了运营商放弃时消息仍未投递的原因,通常是 unreachable:终端在整个期间保持关机或不在覆盖范围内。

屏蔽事件

在逐条消息的生命周期之外,有一个事件报告工作区屏蔽列表的变更:sms_suppression.created 在屏蔽生效时触发,无论是订阅者发送了停止关键词、运营商报告了退订,还是有人手动添加。载荷携带 suppression_id、以 destination 表示的订阅者号码、屏蔽绑定的 originator(SMS 屏蔽是精确的发送方-订阅者配对)、reason 和 workspace_id,便于你的系统无需轮询即可镜像该列表:
代码示例
{
  "type": "sms_suppression.created",
  "timestamp": "2026-08-25T14:52:03.192524705Z",
  "data": {
    "suppression_id": "ssu_01krdgeqcxet5s7t44vh8rt9mg",
    "destination": "+15550001234",
    "originator": "+15557654321",
    "reason": "keyword_stop",
    "workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf"
  }
}
在 Preferences 选项卡中记录的工作区级退订属于偏好声明而非屏蔽,不会触发此事件。

读取消息的时间线

Webhooks 将事件投递到你的系统。若需一次性查看,SMS 日志将相同的事件流渲染为带有时间戳、运营商详情和错误的时间线。若需以编程方式获取时间线,请调用 GET /v1/sms/messages/{message_id}/events。若只需读取最新状态,请调用 GET /v1/sms/messages/{message_id}。

后续步骤

  • Webhooks 与事件:设置端点、验证签名,以及处理重试与回放。
  • SMS 日志:查看这些事件驱动的逐条消息时间线。
  • 发送 SMS:设置每个事件上回显的 tags 和 metadata。