双向

短信会有回复。 那就处理好它们。

设置时长:
Cursor

有人发送到你已开通号码的每条消息都会以 HMAC 签名 webhook 形式送达。读取文本、从同一号码回复,并让 Bird 替你处理 STOP 和 HELP。在你已经在使用的发送 API 上构建会话式流程和自动回复。

send-otp.ts
200 · 0.4s
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

const code = generateOtp();

const { data, error } = await bird.sms.send({
  from:     "Bird",
  to:       "+15005550006",
  text:     `Your Bird verification code is ${code}. Reply STOP to opt out.`,
  category: "authentication",
}).safe();

if (error) throw error;
console.log(data.id);
// → "sms_4kT01Lq2m..."

Today at 2:14 PM

Hey Ada, your Bird sign-in code is 482917. It'll expire in 10 minutes. Don't share it with anyone.
482917
Delivered

入站只是又一个 webhook。

双向通信是 Bird SMS API 的一部分。当有人向您的号码发短信时,您会在同一个已签名、防重放的端点上收到 sms.received 事件——该端点已经在传递您的送达回执。无需第二套集成,也无需轮询:只需验证一次签名,然后按类型进行路由。

监听有回复回来的内容。

一条入站消息和一次退订都是事件,与投递回执相同。验证一个签名,按 type 进行分支,并在你已经写好的处理程序中处理每一个。

app/api/webhooks/bird/route.ts
signed
import { bird } from "@/lib/bird";

export async function POST(req: Request) {
  const event = bird.webhooks.unwrap(
    await req.text(),
    Object.fromEntries(req.headers),
  );

  switch (event.type) {
    case "sms.received":
      await handleInbound(event.data.from, event.data.text);
      break;
    case "sms.opted_out":
      await removeFromCampaigns(event.data.from);
      break;
  }

  return new Response(null, { status: 204 });
}

在每个 Bird 渠道上负载都是相同的信封:一个 evt_ id、一个 HMAC 签名,以及一个防重放的时间戳。

  • sms.received一条入站消息到达您的号码:包含发送者、您的号码和短信内容。
  • sms.delivered你发送的一条回复到达了手机(运营商 DLR)。
  • sms.opted_out发送者发送了 STOP:Bird 已将其退订并阻止后续发送。

一条完整的已签名入站消息。

这就是一个 sms.received 事件在传输中的样子。from 是给你发短信的人,to 是你已开通的号码,而 segments 和 encoding 的报告方式与发送时相同,因此一条很长的入站回复永远不会出乎意料。

sms.received
evt_
{
  "id": "evt_7nQ9xLp2aR...",
  "type": "sms.received",
  "created_at": "2026-06-26T14:03:11Z",
  "data": {
    "id": "sms_5hV02Mr3n...",
    "from": "+15005550006",
    "to": "+14155550172",
    "text": "YES book me in for Thursday",
    "encoding": "GSM-7",
    "segments": 1
  }
}

从同一号码回复。

一次回复就是一次把 from 和 to 互换的发送。把 from 设为你的号码、to 设为原始发送者,会话便始终保持在同一号码上,因此接收方看到的是一个会话线程,而不是每次都换一个新的发送者。在此之上构建自动回复、确认或完整的会话式流程。

reply.ts
200 · reply
async function handleInbound(from: string, text: string) {
  if (/^yes\b/i.test(text)) {
    const { error } = await bird.sms.send({
      from:     "+14155550172", // your two-way number
      to:       from,           // reply to the sender
      text:     "Booked. See you Thursday at 10am.",
      category: "transactional",
    }).safe();

    if (error) throw error;
  }
}

STOP、HELP 和 START 均由我们为你处理。

Bird 会在保留关键字到达你的处理程序之前识别它们:STOP 会将发送方加入你的抑制列表并触发 sms.opted_out,HELP 会返回自动帮助回复,START 则让其重新订阅。之后发往被抑制号码的消息会被自动拦截。你仍然可以为 YES、BOOK 或流程所需的任何内容保留自己的关键字。完整的关键字与退订规则见 退订处理

你接下来会想要的两样东西。

接收入站消息需要支持双向通信的号码:长代码、短代码和免费电话号码可以接收消息,字母数字发送者 ID 则不可以。如需在同一设备上实现更丰富的双向交互(输入指示器、已读回执、轮播卡片),RCS 可在设备支持的情况下升级对话体验。

在文档中深入了解。

为入站事件接入 webhook,阅读 错误参考 了解你将处理的各种失败,并查看 滥用与合规 了解关键词和同意规则。

双向 SMS 常见问题

入站 SMS 如何到达我的应用?
发送到您配置号码的每条消息都会以 HMAC 签名的 sms.received webhook 形式到达。您只需验证一个签名,读取 from、to 和 text 字段,然后将其路由到您自己的逻辑中——与您处理送达回执的信封格式完全相同。
我可以回复一条入站消息吗?
可以。从消息进入时所用的同一号码发送即可回复。把 from 设为你的号码、to 设为原始发送者,整个会话便始终保持在同一号码上。
当有人发送 STOP 时会发生什么?
Bird 会自动将发送者添加到您的退订列表并触发 sms.opted_out 事件,同时自动阻止后续向该号码发送消息。HELP 会返回帮助回复,START 会重新订阅——所有这些都在到达您的代码之前处理完毕,因此无需自行编写关键词逻辑即可保持合规。
双向需要一个特殊号码吗?
你需要一个支持双向的号码。长码、短码和免费电话号码支持入站;字母数字发送者 ID 仅可发送,无法接收回复。

在一个号码、一套 API 上发送和接收。

双向入站是 Bird SMS API 的一项能力:发送、号码、合规、路由和分析都随它一同提供,运行在我们已经运营十年的基础设施之上。

从一个渠道开始。
准备好后,再添加其他渠道。

测试 API 密钥即刻可用。添加支付方式并验证发送者身份后,即可解锁生产环境。

正在使用 Claude Code、Cursor 或 Codex?复制一条设置提示,您的智能代理即可自动安装 Bird CLI 和相关技能。选择您的工具:

Cursor