WhatsApp 链接按钮
链接按钮在 WhatsApp 消息下方放置一个可点击的按钮,点击后会在收件人的浏览器中打开一个 URL。当下一步操作在网页上完成时使用它,例如结账页面或工作坊日期列表,而不是在聊天中完成。如果需要收件人在 WhatsApp 内回复选择,请改用回复按钮或列表菜单。
发送链接按钮
将 interactive.type 设置为 cta_url,使用一个 body_text 和一个 cta_url 对象来承载按钮的 text 和 url:
const msg = await bird.whatsapp.send({
to: "+16505551234",
from: "+13124495648",
interactive: {
type: "cta_url",
body_text: "Tap the button below to see the available dates.",
cta_url: { text: "See dates", url: "https://example.com/workshops?click_id=a1b2c3" },
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+16505551234",
from_="+13124495648",
interactive={
"type": "cta_url",
"body_text": "Tap the button below to see the available dates.",
"cta_url": {"text": "See dates", "url": "https://example.com/workshops?click_id=a1b2c3"},
},
)
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: "cta_url",
BodyText: "Tap the button below to see the available dates.",
CtaUrl: &bird.WhatsAppInteractiveCtaUrlSend{Text: "See dates", Url: "https://example.com/workshops?click_id=a1b2c3"},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$interactive = (new WhatsAppMessageSendRequestInteractive())
->setType('cta_url')
->setBodyText('Tap the button below to see the available dates.')
->setCtaUrl(
(new WhatsAppInteractiveSendCtaUrl())
->setText('See dates')
->setUrl('https://example.com/workshops?click_id=a1b2c3'),
);
$message = $bird->whatsapp->send(
to: '+16505551234',
from: '+13124495648',
interactive: $interactive,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--from +13124495648 \
--interactive '{"body_text":"Tap the button below to see the available dates.","cta_url":{"text":"See dates","url":"https://example.com/workshops?click_id=a1b2c3"},"type":"cta_url"}' \
--to +16505551234{
"name": "whatsapp_send",
"arguments": {
"from": "+13124495648",
"interactive": {
"body_text": "Tap the button below to see the available dates.",
"cta_url": {
"text": "See dates",
"url": "https://example.com/workshops?click_id=a1b2c3"
},
"type": "cta_url"
},
"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": "cta_url",
"body_text": "Tap the button below to see the available dates.",
"cta_url": {
"text": "See dates",
"url": "https://example.com/workshops?click_id=a1b2c3"
}
}
}'每条服务消息都必须包含 from:一个你的工作区拥有的号码,而不是 Bird 托管的号码。完整结构还可添加可选的标题、页脚以及对早前消息的引用:
代码示例
{
"to": "+16505551234",
"from": "+13124495648",
"in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
"interactive": {
"type": "cta_url",
"header": {
"type": "image",
"url": "https://cdn.example.com/banners/workshop.png"
},
"body_text": "Tap the button below to see the available dates.",
"footer_text": "Dates are subject to change.",
"cta_url": {
"text": "See dates",
"url": "https://example.com/workshops?click_id=a1b2c3"
}
},
"tags": [{ "name": "campaign", "value": "autumn-workshops" }],
"metadata": { "order_id": "A-4192" }
}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 是可选的,会在按钮下方添加一行文字。
限制
| 字段 | 约束 |
|---|---|
| cta_url 按钮 | 恰好一个 |
| cta_url.text(标签) | 必填,1 到 20 个字符 |
| cta_url.url | 必填,1 到 2000 个字符 |
| body_text | 必填,1 到 1024 个字符 |
| footer_text | 可选,1 到 60 个字符 |
| header.text | 1 到 60 个字符 |
url 的 2000 字符上限是 Bird 自身的限制:Meta 未公布此字段的长度限制。url 还要求 format: uri 是带协议的绝对地址,但 Bird 不检查具体协议:http:// 地址可以通过 Bird 的验证,而 Meta 是判断其能否送达的唯一裁定者。
点击会回传什么
点击会在收件人的浏览器中打开该地址,但不会通过 API 向你回传任何信息。链接按钮的点击不是 interactive_reply:生成 interactive_reply 的入站映射器只处理回复按钮点击和列表行点击,cta_url 链接没有对应的入站结构。你能看到的是常规的出站生命周期,即消息的 sent、delivered 和 read 状态,但 read_at 只表示消息被打开了,而不是按钮被点击了。没有点击事件、没有时间戳,WhatsApp 和 Bird 也不提供任何按收件人维度的点击信号。
两种获取归因的方法,因为发送本身不会提供归因信息:
- 在落地页上埋点。 唯一可用的点击证据在你自己的目标服务器上,来自你分发出去的 URL。
- 自行为每个收件人生成不同的 URL。 你发送的 url 是一个字面字符串:Bird 原样存储并传递给 Meta,不做替换,也没有变量语法。同一次发送中它对每个收件人都完全相同,因此按收件人归因意味着你需要自行生成查询参数(例如 ?click_id=<value>),并为每个收件人发起一次 POST /v1/whatsapp/messages 调用。该端点每次调用本就只接受一个 to,所以这只是你侧的记录工作,而非 API 缺少的功能。
第三种选择完全在此类型之外:带有 url 按钮变量的模板由 WhatsApp 本身为每个收件人个性化,通过发送请求的 button 组件提供。该变量必须位于地址末尾,写作 {{1}},因此它可以改变尾部路径段或查询值,但不能改变主机名或 URL 中间部分。权衡:模板提供按收件人的 URL 和客服窗口外的投递能力,代价是需要 Meta 审核和固定的已批准结构;而 cta_url 发送提供自由格式、无需审核的窗口内发送,URL 由你自行变化。
限制和边界情况
- 客服窗口必须处于开启状态。 链接按钮是服务消息,只能在窗口开启期间投递;参见中心的客服窗口。窗口检查采用开放式失败策略,因此 202 并不能证明发送时窗口确实是开启的。
- from 必须是你的工作区拥有的号码。 省略它或指定一个未连接的发送号码,请求会在创建发送之前被拒绝。
- URL 在整个发送中是静态的,对每个收件人完全相同。 此类型没有按收件人的变量。参见点击会报告什么了解如何进行点击归因。
- 永远没有点击信号。 链接按钮的点击不会产生入站消息,也不会产生 webhook 事件。不要构建仅依赖此类型就承诺点击指标的功能。
- Bird 检查 URL 的格式,而非协议。url 必须是带协议的绝对地址,但 Bird 不要求 https,Meta 也未公布协议限制。相比之下,媒体标题的 url 文档要求必须是 https。
- WhatsApp 无法获取的媒体头部 URL 会在发送被接受后失败。 WhatsApp 在发送时获取头部资源并缓存 10 分钟;签名 URL 的有效期必须长于发送耗时,不可达的 URL 将异步失败,消息的 last_error 上会出现 media_rejected。
中心的错误表中列出的结构检查均不会在此类型上触发:它们检查的是列表的行、buttons 数组或轮播的卡片,而 cta_url 消息不包含这三者中的任何一种。结构错误(例如 text 标签超过 20 个字符)会返回通用的请求验证错误,而不是那些特定错误码。无法解析的引用会在创建或计费之前使请求失败:当 id 指定的消息不属于此工作区时返回 404 E15071,当 id 指定的消息无法被引用时返回 422 E15072。对于所有 WhatsApp 发送可能遇到的错误(窗口关闭、发送号码缺失或无效、收件人无效),请参阅中心的错误和发送 WhatsApp 消息。
后续步骤
- WhatsApp 互动消息:六种互动类型的共同内容
- WhatsApp 模板:使用 url 按钮变量,由 WhatsApp 为每个收件人个性化
- 发送 WhatsApp 消息:请求信封、202 模型和安全重试