通过同一套 SMS API 发送一条消息或一百条。SDK 会在发送前计算段数,替你选择 GSM-7 或 Unicode,而且每次发送都是幂等的,并在每个投递状态上提供 webhook。
import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });
const { data, error } = await bird.sms.send({
from: "Bird",
to: "+31612345678",
text: "Your order #4821 has shipped. Track it: bird.ly/t/4821x",
category: "transactional",
}).safe();
if (error) throw error;
console.log(data.id);
// → "sms_01m11jw130e7svjzv70kgqr38w"
五分钟内发送你的第一条 SMS。
用你已经在使用的语言。
发送是 Bird SMS API 的核心。第一次发送会发往一个官方测试接收方(+15005550006),因此你可以在开通号码之前交付一项 CI 检查并接入 webhook。
const msg = await bird.sms.send({
from: "+15557654321",
to: "+14155550100",
text: "Your verification code is 123456.",
category: "authentication",
});
console.log(msg.id, msg.status);五件你无需自己构建的事。
每个 Bird 渠道上相同的约定。
- 01
发送前计算段数。
SDK 会测量编码后的长度并告诉你一条消息要花费多少段,因此一个意外字符永远不会悄无声息地把一条短信拆成三条。
- 02
GSM-7 和 Unicode,替你决定。
纯文本走 GSM-7;一个表情符号或非拉丁文字会把整条消息翻转为 UCS-2。Bird 会选择编码,并在单个字符改变成本时提醒你。
- 03
在一次调用中批量发送。
在一个请求中发送许多相互独立的消息,每条都有各自的接收方和文本,并作为一个整体进行校验,因此你永远不会发送一半。
- 04
按约定幂等。
每次发送都接受一个幂等键,因此超时后重试的请求会返回原始结果,而不是给某人发两次短信。
- 05
在每次状态变更上提供一个 webhook。
已排队、已发送、已投递、失败。每一个都经过 HMAC 签名、防重放、幂等,在每个渠道上都是相同的信封。
已经在别处发送了?切换客户端,保留调用。
结构几乎不变:换掉客户端,保留你的 from、to 和 text,把你的 webhook 指向一个端点。与你的邮件、语音和 WhatsApp 发送使用相同的鉴权模型。
import twilio from "twilio";
const client = twilio(accountSid, authToken);
await client.messages.create({
from: "+14155550172",
to: "+15005550006",
body: "Your code is 123456.",
});import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });
await bird.sms.send({
from: "+14155550172",
to: "+15005550006",
text: "Your code is 123456.",
category: "authentication",
});在运营商之前就知道成本。
A GSM-7 message fits 160 characters per segment; a single emoji or non-Latin character flips the whole message to UCS-2 and drops that to 70. Bird counts the segments when it accepts the send and returns the breakdown in the response, so the number you are billed on is in hand before the carrier ever sees the message, and a concatenated message is always a deliberate choice.
const { data, error } = await bird.sms.send({
from: "Bird",
to: "+31612345678",
text: "Your code is 123456.",
category: "authentication",
}).safe();
if (error) throw error;
console.log(data.segments);
// → { characters: 20, count: 1, encoding: "GSM_7BIT" }
一条消息或一百条,一次调用。
在一个请求中批量发送相互独立的消息,每条都有各自的接收方和文本。批次会作为一个整体进行校验:一个坏号码会以 422 拒绝整个调用,因此你永远不会发送一半。一个幂等键让整个请求可安全重试。
const { data: batch, error } = await bird.sms
.sendBatch(
users.map((u) => ({
from: "Bird",
to: u.phone,
text: `Hi ${u.name}, your appointment is tomorrow at ${u.time}.`,
})),
{ idempotencyKey: `reminders-${runId}` },
)
.safe();
if (error) throw error;
console.log(`queued ${batch.data.length} messages`);观察每条消息贯穿其完整生命周期。
一次发送会立即返回 202;结果会以 webhook 形式送达。验证一个签名,按 type 进行分支:与你已经为邮件、语音和 WhatsApp 处理的相同信封。
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.delivered":
await markDelivered(event.data.sms_id);
break;
case "sms.failed":
await flag(event.data.to, event.data.error?.description);
break;
}
return new Response(null, { status: 204 });
}失败的发送和 STOP 回复会自动更新你的抑制列表,因此一个坏号码永远不会让你付出两次代价。
sms.accepted已被 API 接受并排队等待交付给运营商。sms.sent已提交给目的地运营商的 SMSC。sms.delivered已从运营商收到投递回执(DLR)。sms.failed永久失败:运营商拒绝、号码无效或触发屏蔽规则。