向 WhatsApp 群组发送消息
群组发送就是一条普通的 POST /v1/whatsapp/messages,只是 to 指定的是群组而非个人:一次请求、一条消息,群聊中的每位参与者都会收到并可以在其他人可见的地方回复。不同之处在于报告方式。消息会携带计数器,显示有多少人收到了消息,投递确认则按参与者逐一到达。
创建和管理群组与向群组发送消息是分开的。管理 WhatsApp 群组介绍了如何通过 API 创建群组并分享邀请链接,WhatsApp 群组介绍了群组的用途以及 WhatsApp 对其施加的限制。
前提条件
你需要一个具有 WhatsApp 写入权限的 API 密钥,以及一个 Active 状态群组的 ID(wag_…)。可以从 Groups 页面群组的 Details 选项卡中复制,从通过该群组收到的消息上的 to.group_id 中读取,或列出你的群组。
将示例中的群组 ID 替换为你自己的。使用 TypeScript、Python、Go 或 PHP SDK 指南为你的语言初始化客户端。对于 CLI 示例,请安装并认证 CLI,授予 WhatsApp 写权限。在 cURL 请求中使用与你的工作区区域对应的 API 主机。
1. 发送消息
将群组 ID 放入 to,不要设置 from。群组绑定到创建它时使用的商业号码,因此消息只能从该号码发出;指定发送者会返回 422 E15018。
const msg = await bird.whatsapp.send({
to: "wag_01krdgeqcxet5s7t44vh8rt9mg",
text: { body: "The route sheet for Tuesday is up." },
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="wag_01krdgeqcxet5s7t44vh8rt9mg",
text={"body": "The route sheet for Tuesday is up."},
)
print(msg.id, msg.status)msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "wag_01krdgeqcxet5s7t44vh8rt9mg",
Text: &bird.WhatsAppTextSend{Body: "The route sheet for Tuesday is up."},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)$text = (new WhatsAppMessageSendRequestText())
->setBody("The route sheet for Tuesday is up.");
$message = $bird->whatsapp->send(
to: 'wag_01krdgeqcxet5s7t44vh8rt9mg',
text: $text,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--text 'The route sheet for Tuesday is up.' \
--to wag_01krdgeqcxet5s7t44vh8rt9mgcurl -X POST "https://us1.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "wag_01krdgeqcxet5s7t44vh8rt9mg",
"text": { "body": "The route sheet for Tuesday is up." }
}'API 返回 202,群组信息回显在 to.group_id 上,以及 status: accepted 和 recipient_count:发送被接受时群组中有多少人。该计数是第 3 步中所有内容的分母,并在此刻固定。在消息传输过程中通过邀请链接加入的人不会收到该消息,也不会改变计数。
2. 群组支持的内容
群组支持文本、图片、视频、音频、贴纸、文档、位置、联系人名片,以及你的工作区创建的除身份验证类别外任何类别的模板。两种内容会在消息创建或计费之前被拒绝,返回 422 E15052,因为 WhatsApp 不会将这两种内容投递到群聊中:
- 任何交互式内容:回复按钮、列表菜单、链接按钮、轮播卡片,以及位置和联系信息请求。
- 身份验证模板。 请将一次性验证码直接发送给参与者。
Bird 托管模板从 Bird 拥有的号码发送,该号码永远不是群组绑定的号码,因此向群组发送托管模板会返回 422 E15001。
自由格式内容仍然需要一个打开的客服服务窗口,群组有自己的服务窗口:任何参与者向群组发送消息都会为整个群组打开一个 24 小时窗口,而该参与者在群组外向你发送消息不会打开此窗口。窗口过期后,只有模板才能到达群组。
3. 跟踪扇出
检索消息以查看投递进度。三个计数器报告扇出情况:
| 字段 | 报告内容 |
|---|---|
recipient_count | 接受时的参与者数,其余两个计数器的分母 |
delivered_count | WhatsApp 已确认消息送达的人数,包括仅报告了已读的人 |
read_count | 已打开消息的人数 |
在群组消息中,status 报告的是每位收件人到达的最远状态:只有当 delivered_count 等于 recipient_count 时才会变为 delivered,在部分人已确认而其他人尚未确认时保持 sent。WhatsApp 消息没有 read 状态,因此已读通过 read_count 和 read_at 体现。delivered_at 和 read_at 取自第一个收件人,而非最后一个。failed 和 rejected 不按参与者区分,因为只有一次向 WhatsApp 的交接,也只有一种被拒绝的方式。
向一个尚无人加入的群组发送消息时,不会携带任何计数器,因为没有可报告的分母。因此用 to.group_id 而非计数器来区分群组消息和一对一消息。
要查看某条确认对应的是哪位参与者,请列出该消息的事件。群发消息会为每位参与者展开为最多一个 whatsapp.delivered 和最多一个 whatsapp.read,每个都在 recipient 中携带该参与者的电话号码、其业务范围用户 ID,或两者兼有。两者对任何人都不保证一定出现:如果参与者已在查看聊天,WhatsApp 会跳过送达回执;已读回执仅在对方打开消息时才会到达。统计实际收到的回执,而不是等待每位参与者各收到一条,总数请读取计数器。单个 whatsapp.sent 事件不携带 recipient:那是一次向 WhatsApp 的交接,不会指明任何人。whatsapp.delivered 和 whatsapp.read webhooks 携带相同的字段,这也是区分其他方面完全相同的回调的方式。
4. 读取一个群组的对话
将 group_id 传给消息列表以获取一个群组的双向消息记录:
for await (const msg of bird.whatsapp.list({ group_id: "wag_01krdgeqcxet5s7t44vh8rt9mg" })) {
console.log(msg.id, msg.direction, msg.status);
}for msg in client.whatsapp.list(group_id="wag_01krdgeqcxet5s7t44vh8rt9mg"):
print(msg.id, msg.direction, msg.status)for msg, err := range client.Whatsapp.List(context.Background(), bird.WhatsappListParams{
GroupID: "wag_01krdgeqcxet5s7t44vh8rt9mg",
}) {
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Direction, *msg.Status)
}foreach ($bird->whatsapp->list(['group_id' => 'wag_01krdgeqcxet5s7t44vh8rt9mg']) as $message) {
echo $message->getId(), ' ', $message->getDirection(), "\n";
}bird whatsapp list --group-id wag_01krdgeqcxet5s7t44vh8rt9mgcurl "https://us1.platform.bird.com/v1/whatsapp/messages?group_id=wag_01krdgeqcxet5s7t44vh8rt9mg" \
-H "Authorization: Bearer $BIRD_API_KEY"入站群组消息读取时会在 from 上显示发送者,以及一个同时携带你的商业号码和 group_id 的 to:接收号码以它所在的群组为限定条件。to 和 from 都无法匹配群组,因此 group_id 是唯一能将列表缩小到单个群组的筛选条件。相同的消息也在控制台的WhatsApp 日志中。
费用
群组发送按发送 WhatsApp 消息中描述的两个组成部分计费,每个部分的定价方式有所不同。Bird 的费用按一次发送收取一次,按消息发出所用商业号码所在国家定价,因为群组可能跨越多个国家,没有单一的收件人国家。Meta 的份额按消息送达的每位参与者累计,每位按该参与者所在国家的普通一对一费率定价,因此 passthrough_amount 随着回执到达而增长。从 2026 年 10 月 1 日起,该份额还涵盖发送到群组的自由格式内容,Meta 按每位送达的参与者收费,从发送号码每月 1,000 条免费服务消息中扣除:2026 年 10 月定价变更。
故障排除
404(E15046):该群组 ID 未指向此工作区持有的任何群组。群组属于创建它的工作区,因此来自其他工作区的 ID 在此处找不到。409(E15047):群组处于待处理、暂停、已删除或失败状态。只有 Active 状态的群组才能接收消息,群组在 WhatsApp 确认之前一直处于待处理状态。422(E15018):移除from。群组使用创建时绑定的号码发送。422(E15052):交互式内容或身份验证模板。参阅群组支持的内容。422(E15044):群组的服务窗口已关闭。发送模板,或等待参与者向群组发送消息。status停留在sent:少于recipient_count位参与者已确认投递。查看消息的事件以了解哪些人尚未确认。
后续步骤
- 接收 WhatsApp 群组消息:识别发送者并回复群组
- 管理 WhatsApp 群组:管理参与者、邀请链接和加入请求
- 消息状态 webhook:接收消息的投递状态更新
- 业务范围用户 ID:识别您没有电话号码的参与者