Sign inGet Started

发送 SMS

本指南介绍单条发送端点 POST /v1/sms/messages。构建一个包含收件人、发送方、正文和类别的 JSON 载荷。Bird 返回 202 Accepted 及消息 ID,并异步投递。每个请求向一个收件人发送一条消息。如需一次发送多条消息,请使用批量发送。如需发送模板而非自定义文本,请用 template 对象替代 text、category 和 from。

发送前:启用目的地国家

您的工作区有一个默认拒绝的目的地白名单,初始仅启用您所在组织的本国。Bird 会在解析发送方之前,以 422 SMSDestinationNotEnabled 拒绝发往其他国家的请求。请在控制台的 SMS > Destinations 中启用您服务的国家。

最简发送

最小的有效自由文本载荷包含一个 to 收件人、一个 from 发送方、一个 text 正文和一个 category。
const msg = await bird.sms.send({
  from: "+15557654321",
  to: "+14155550100",
  text: "Your verification code is 123456.",
  category: "authentication",
});
console.log(msg.id, msg.status);
使用您的区域主机(https://us1.platform.bird.com 或 https://eu1.platform.bird.com)并搭配对应的 bk_{region}_... 密钥。响应为已接受的消息:
代码示例
{
  "id": "sms_01ky7qmwgpfkybj9ecrnwjx714",
  "direction": "outbound",
  "status": "accepted",
  "to": "+31612345678",
  "from": "Bird",
  "text": "Your Bird verification code is 481920. It expires in 10 minutes.",
  "category": "authentication",
  "segments": { "count": 1, "encoding": "GSM_7BIT", "characters": 64 },
  "cost": null,
  "carrier": null,
  "mcc_mnc": null,
  "sent_at": null,
  "delivered_at": null,
  "created_at": "2026-07-23T14:56:34.326Z"
}
status: accepted 表示 Bird 已收到消息并正在处理;cost 为 null,因为计价在处理期间完成。后续流程请参阅异步模型。

构建载荷

收件人

to 是一个 E.164 格式的收件人:以 + 开头,后接国家代码和用户号码,例如 +31612345678。一条消息只发给一个收件人,不支持 cc、bcc 或收件人数组。如需发送给多人,请使用批量发送。

发送方

from 在自由文本发送中为必填字段,是收件人看到的发送方。它有两种形式,哪种可用取决于目的地国家:
  • 字母数字发送者 ID:3 到 11 个字母、数字、空格、短横线、下划线或点,至少包含一个字母,且首尾不能是分隔符,例如 Bird 或 Acme-Co。必须包含字母,因此纯数字加标点的字符串(如 555 555)会被拒绝。部分国家要求注册,其他国家(包括美国)不支持字母数字发送者。收件人无法回复此类发送者。
  • 您的工作区拥有的号码,使用 E.164 格式或纯数字。任何全数字的 from 都会被视为数字号码,并在您的发送方中查找,因此您不拥有的任意号码会被拒绝。它作为长号码、免费电话号码还是短号码使用,取决于号码本身,而非您输入了多少位数字。一个 6 位的 from 并非因为有 6 位数字就是短号码;只有当您拥有的号码本身是短号码时,它才是短号码。
对目的地无效的发送方会被拒绝,返回 422 并注明原因(例如在不支持字母数字发送方的地区返回 SMSAlphaNotSupported)。在模板发送中,不接受 from:Bird 会根据目的地和类别自动选择发送方。
申请发送方 ID、查看各国对发送方的要求以及按国家注册的流程,请参阅 SMS 发送方 ID。

正文和类别

text 是消息正文,至少一个字符。它按分段计费和投递;每次发送上限为 12 个分段(约 1,836 个 GSM-7 字符,若正文使用扩展 UCS-2 编码则为 804 个字符)。超出上限的正文会以 422 被拒绝,而非被截断。
category 在自由文本发送中为必填字段,将消息分类为 transactional、marketing、authentication 或 service。它告知 Bird 和运营商您发送消息的原因。一次性验证码使用 authentication;促销消息使用 marketing。请选择与消息用途匹配的类别。

标签和元数据

两者都用于将您自己的数据附加到发送中,但用途不同:
  • tags 是结构化的 {name, value} 键值对(每次发送最多 20 个;名称 1 到 32 个字符,值 1 到 64 个字符,仅限 ASCII [A-Za-z0-9_-],区分大小写,名称在同一次发送中唯一)。它们是一等过滤维度:可以按标签过滤消息列表。适用于低基数标签,如 campaign 或 experiment_variant。
  • metadata 是任意 JSON 对象(序列化后最大 2 KB)。它会被存储,在 API 读取时返回,并在每个 webhook 事件中回传,但不是过滤维度。适用于往返上下文:内部 ID、外键,以及您希望在每个事件中回传的任何数据。
代码示例
{
  "tags": [{ "name": "campaign", "value": "spring-2026" }],
  "metadata": { "user_id": "usr_12345", "order_id": "ord_98765" }
}

字段参考

字段类型必填限制 / 说明
tostring (E.164)是每条消息一个收件人
fromstring是*自有 E.164 号码、字母数字发送方 ID(3–11 个字符,至少一个字母)或短号码(5–6 位数字)
textstring是*至少 1 个字符;上限 12 个分段
categorystring是*transactional、marketing、authentication 或 service
tags{name, value}[]否最多 20 个;名称 1–32 个字符,值 1–64 个字符;仅限 [A-Za-z0-9_-]
metadataobject否任意 JSON,序列化后最大 2 KB
optionsobject否每条消息的处理设置。smart_encoding 是唯一可用的选项;参见分段和编码
* 在自由文本发送中为必填。模板发送会由模板提供正文、类别和发送方,并拒绝这三个字段。

使用模板发送

无需自行编写 text,而是将发送的 template 对象设置为引用 Bird 的内置模板之一。模板提供正文、类别和发送方,因此 text、category、from 和 media_urls 不能同时使用。模板目录、每个模板的变量以及完整的模板发送约定请参阅 SMS 模板。

分段和编码

SMS 按分段计费。适合 GSM-7 编码的消息,单个分段可容纳 160 个字符;UCS-2(由表情符号、中日韩文字或其他非 GSM 字符触发)降至 70 个字符。较长的消息会被拆分为多段,每段容量略低。每个响应都会报告解析后的 segments:计费 count、encoding 和字符数。分段是计费单位;参见费用。
当排版字符是正文超出 GSM-7 范围的唯一原因时,智能编码可以减少分段数。将 options.smart_encoding 设置为 true,Bird 会在发送前将弯引号、破折号、省略号等字符替换为 GSM-7 等效字符。此功能默认关闭,因为它会更改您编写的正文。
完整字符集、扩展表中占两个位置的字符、表情符号大小、智能编码的替换内容以及分段计算方式,请参阅字符限制。

批量发送

POST /v1/sms/batches 可在一次请求中发送最多 100 条独立消息。批量请求使用 sms_batch 请求速率限制策略,与单条发送的 sms_send 策略相互独立。请求体是一个 JSON 对象,其 messages 数组包含构建载荷中所述的消息对象:
const result = await bird.sms.sendBatch({
  messages: [
    {
      from: "+15557654321",
      to: "+15551111111",
      text: "Hi Alice!",
      category: "marketing",
    },
    {
      from: "+15557654321",
      to: "+15552222222",
      text: "Hi Bob!",
      category: "marketing",
    },
  ],
});
校验采用全有或全无策略:如果批次中任何一条消息无效,整个请求将以 422 被拒绝且不会发送任何消息,因此批次不会部分执行。成功时,202 响应按提交顺序在 data 下返回每条已接受的消息,并附带包含 accepted_count 的 summary。此后每条消息彼此独立:一个收件人的失败不会影响其他消息。

异步模型:202 的含义

发送成功后返回 202 Accepted,包含消息 ID 和 status: accepted。请求失败会立即返回:无效字段、正文超出分段上限、未启用的目的地国家或无效发送方会返回 422。工作区余额不足时会收到 402。
投递异步进行。当 Bird 将消息交给运营商时,消息状态变为 sent。随后,投递回执通过事件和 Webhook 及读取端点将状态设为 delivered、undelivered、failed 或 expired。这一设计带来三个影响:
  • 费用在接受后定价。 消息上的 cost 在接受时为 null,待 Bird 在处理过程中完成定价后填充。回读该消息或等待投递事件即可查看当前已定价的费用;费用与计费介绍了各费用组成部分及某项保持未定价的情形。
  • 消息可能在 202 之后被拒绝。 如果处理过程中扣费失败,消息最终状态为 rejected,并触发 sms.rejected Webhook,且不会向您计费;余额耗尽表现为 last_error.code: insufficient_balance。
  • 读取可能短暂滞后于 202。 消息在 202 之后不久才在读取端点可见,因此发送后立即执行 404 查询会在片刻内自行解决。

保留字段

Bird 当前会以 422 SMSUnsupportedFeature 拒绝以下请求字段:
scheduled_at、validity_period、media_urls、messaging_profile_id、broadcast_id、campaign_id、audience_id、contact_id、topic_id、personalization、options.max_price_per_segment、options.track_clicks
发送时不要包含这些字段。

安全重试

在每次逻辑发送中附带唯一值的 Idempotency-Key 请求头。如果请求成功但未返回响应,使用相同的请求和密钥重放即可。Bird 会返回原始结果,而不会重复发送消息。有关密钥格式和保留期限,请参阅幂等性。

费用与计费

出站 SMS 按分段计费。您支付的金额取决于目的地国家和运营商;某些路由会附加第三方附加费,例如美国 10DLC 运营商费用。
消息的 cost 将费用拆分为命名组成部分。transaction_amount 是 Bird 承载消息所收取的费用,passthrough_amount 是转嫁的第三方费用,amount 是已定价组成部分的总和,币种为 currency_code。尚未定价的组成部分为 null 而非 "0.00000",因此附加费始终未能解析的消息,amount 仅为承载费用。消息参考记录了每个字段。
附加费为尽力解析。Bird 在记录投递回执时在限定窗口内解析附加费。如果在该窗口内未能解析,passthrough_amount 将永久保持 null:Bird 不会重试,amount 仍为承载费用。
入站 SMS 分两项计费:按分段的入站费率和适用时的入站运营商附加费。两项均在接收消息自身的 cost 中报告:费率为 transaction_amount,附加费为 passthrough_amount。与出站附加费不同,入站附加费在消息被接受时即完成定价,而非在投递时,因此不会后续补充。
在 SMS 日志中查看每条消息的费用和分段。

后续步骤

  • SMS 模板:发送内置模板并让 Bird 选择发送方。
  • SMS 日志:查找消息并检查其生命周期、分段和费用。
  • 事件:在您的系统中接收投递事件。
  • SMS 指标:监控投递率、失败率和已接受量。
  • 幂等性:使用 Idempotency-Key 请求头安全重试。
  • 发送您的第一条 SMS:一段视频,演示在控制面板中完成相同设置的过程