双向

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

设置时长:
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.`,
}).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 进行分支。

监听有回复回来的内容。

一条入站消息和一次退订都是事件,与投递回执相同。验证一个签名,按 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.",
    }).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