WhatsApp 快捷回复按钮
快捷回复按钮在 WhatsApp 消息下方放置最多三个可点击选项,让收件人通过点击而非自由输入来回复。适用于快速决策场景,例如确认或取消预订。如果选项超过三个,请改用列表菜单。
发送快捷回复按钮
将 interactive.type 设置为 button,包含一个 body_text 和一到三个 buttons,每个都是一个 quick_reply:
const msg = await bird.whatsapp.send({
to: "+16505551234",
from: "+13124495648",
interactive: {
type: "button",
body_text: "Your gardening workshop is scheduled for 9am tomorrow.",
buttons: [{ type: "quick_reply", quick_reply: { slug: "change-booking", text: "Change" } }],
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+16505551234",
from_="+13124495648",
interactive={
"type": "button",
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"buttons": [{"type": "quick_reply", "quick_reply": {"slug": "change-booking", "text": "Change"}}],
},
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "+16505551234",
From: "+13124495648",
Interactive: &bird.WhatsAppInteractiveSend{
Type: "button",
BodyText: "Your gardening workshop is scheduled for 9am tomorrow.",
Buttons: &[]bird.WhatsAppInteractiveButtonSend{
{Type: "quick_reply", QuickReply: &bird.WhatsAppInteractiveQuickReplyButtonSend{Slug: "change-booking", Text: "Change"}},
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$interactive = (new WhatsAppMessageSendRequestInteractive())
->setType('button')
->setBodyText('Your gardening workshop is scheduled for 9am tomorrow.')
->setButtons([
(new WhatsAppInteractiveButtonSend())
->setType('quick_reply')
->setQuickReply((new WhatsAppInteractiveButtonSendQuickReply())->setSlug('change-booking')->setText('Change')),
]);
$message = $bird->whatsapp->send(
to: '+16505551234',
from: '+13124495648',
interactive: $interactive,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--from +13124495648 \
--interactive '{"body_text":"Your gardening workshop is scheduled for 9am tomorrow.","buttons":[{"quick_reply":{"slug":"change-booking","text":"Change"},"type":"quick_reply"}],"type":"button"}' \
--to +16505551234{
"name": "whatsapp_send",
"arguments": {
"from": "+13124495648",
"interactive": {
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"buttons": [
{
"quick_reply": {
"slug": "change-booking",
"text": "Change"
},
"type": "quick_reply"
}
],
"type": "button"
},
"to": "+16505551234"
}
}curl -X POST "https://{region}.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "+16505551234",
"from": "+13124495648",
"interactive": {
"type": "button",
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"buttons": [
{ "type": "quick_reply", "quick_reply": { "slug": "change-booking", "text": "Change" } }
]
}
}'from 在每条服务消息中都是必填项:必须是您的工作区拥有的号码,而不是 Bird 托管的号码。完整结构还可以添加可选的标题、页脚、对早前消息的引用,以及第二个按钮:
代码示例
{
"to": "+16505551234",
"from": "+13124495648",
"in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
"interactive": {
"type": "button",
"header": {
"type": "image",
"url": "https://cdn.example.com/banners/workshop.png"
},
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"footer_text": "Lucky Shrub, your gateway to succulents",
"buttons": [
{ "type": "quick_reply", "quick_reply": { "slug": "change-booking", "text": "Change" } },
{ "type": "quick_reply", "quick_reply": { "slug": "cancel-booking", "text": "Cancel" } }
]
},
"tags": [{ "name": "category", "value": "booking" }],
"metadata": { "order_id": "A-1" }
}in_reply_to_message_id 引用同一会话中的早前消息。请参阅中心的引用消息以关联回复,了解解析方式及其可能遗漏的情况。
标题和页脚
标题是可选的,有以下四种形式之一:
代码示例
"header": { "type": "text", "text": "New workshop dates" }
"header": { "type": "image", "url": "https://cdn.example.com/a.png" }
"header": { "type": "video", "url": "https://cdn.example.com/a.mp4" }
"header": { "type": "document", "url": "https://cdn.example.com/a.pdf" }媒体标题(image、video 或 document)通过公开的 https URL 携带文件,由 WhatsApp 在发送时获取,而不是使用已上传的媒体句柄。footer_text 是可选的,会在按钮下方添加一行文字。
限制
| 字段 | 约束 |
|---|---|
| buttons | 1 到 3 个条目,每个都是 quick_reply |
| quick_reply.slug | 必填,1 到 256 个字符 |
| quick_reply.text(标签) | 必填,1 到 20 个字符,在消息内唯一 |
| body_text | 必填,1 到 1024 个字符 |
| footer_text | 可选,1 到 60 个字符 |
| header.text | 1 到 60 个字符 |
Bird 会检查按钮标签(quick_reply.text)是否唯一,但不会检查 slug 值是否唯一,尽管每个 slug 本应标识一个按钮。两个共享同一 slug 的按钮都能发送和投递,但它们的回复无法区分。
读取回复
按下按钮会作为一条独立的入站消息到达,携带 interactive_reply:
代码示例
{
"id": "wam_01kyb2m4xq7whs0d8n3prv6tez",
"direction": "inbound",
"from": { "phone_number": "+16505551234" },
"to": { "phone_number": "+13124495648" },
"status": "received",
"in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
"interactive_reply": {
"type": "button",
"button": {
"slug": "cancel-booking",
"text": "Cancel"
}
},
"created_at": "2026-08-25T09:04:11Z"
}您在发送时设置的 slug 会原样返回,因此可以直接用它进行分支判断,无需查找表。您可以通过消息列表或 GET /v1/whatsapp/messages/{id} 查看此回复;请参阅中心的读取回复了解完整流程。
限制和边界情况
- 客户服务窗口必须处于打开状态。 快捷回复按钮是服务消息,仅在窗口打开时可投递;请参阅中心的客户服务窗口。窗口检查采用开放失败策略,因此 202 并不能证明发送时窗口确实处于打开状态。
- from 必须是您的工作区拥有的号码。 省略该字段或指定一个未连接的发送号码,请求会在创建发送之前被拒绝。
- 标签必须唯一,否则发送会被拒绝。 两个具有相同 quick_reply.text 的按钮会以 422 E15056 WhatsAppInteractiveDuplicateLabel 失败,因为 Meta 会在发送已被接受并计费后拒绝重复标签。
- 标签是收件人看到的内容;slug 永远不会显示。 将面向用户的文案放在 slug 中不会产生任何效果,因为只有 text 会在聊天中渲染。
- WhatsApp 无法获取的媒体请求头 URL 会在发送被接受后失败。 Bird 不会像验证媒体消息 URL 那样验证请求头 url,因此 http:// URL 或返回错误的 URL 会通过请求,然后异步失败,media_rejected 会出现在消息的 last_error 上。
- 发送 Meta 自有的字段名会导致请求失败。 此类型会直接拒绝未知属性,因此从 Meta Cloud API 参考文档复制的 JSON(例如 body 对象或 action.buttons 包装器)需要先重新整理为 Bird 的扁平字段。
无法解析的引用会在创建或计费之前导致请求失败:当 id 指定的消息不属于此工作区时返回 404 E15071,当指定的消息无法被引用时返回 422 E15072。对于任何 WhatsApp 发送可能遇到的错误(窗口关闭、发送方缺失或无效、收件人无效),请参阅中心的错误和发送 WhatsApp 消息。
后续步骤
- WhatsApp 互动消息:六种互动类型的共同要素
- 列表菜单:适用于超过三个选项的场景
- 发送 WhatsApp 消息:请求信封、202 模型和安全重试