# WhatsApp 链接按钮

链接按钮在 WhatsApp 消息下方放置一个可点击的按钮，点击后会在收件人的浏览器中打开一个 URL。当下一步操作在网页上完成时使用它，例如结账页面或工作坊日期列表，而不是在聊天中完成。如果需要收件人在 WhatsApp 内回复选择，请改用[回复按钮](/docs/guides/whatsapp/message-types/interactive/reply-buttons)或[列表菜单](/docs/guides/whatsapp/message-types/interactive/list-menus)。

## 发送链接按钮

将 `interactive.type` 设置为 `cta_url`，使用一个 `body_text` 和一个 `cta_url` 对象来承载按钮的 `text` 和 `url`：

**TypeScript**

```typescript
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);
```

Examples: [TypeScript](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.ts.md) · [Python](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.py.md) · [Go](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.go.md) · [PHP](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.php.md) · [CLI](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.cli.md) · [MCP](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.mcp.md) · [cURL](/zh-sg/wendang/guides/whatsapp/message-types/interactive/cta-url-buttons.curl.md)

每条服务消息都必须包含 `from`：一个你的工作区拥有的号码，而不是 Bird 托管的号码。完整结构还可添加可选的标题、页脚以及对早前消息的引用：

```json
{
  "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` 引用同一对话中的早前消息。请参阅中心的[引用消息以关联回复](/docs/guides/whatsapp/message-types/interactive#quoting-a-message-to-correlate-a-reply)，了解解析的工作方式及其局限性。

此类型只发送一个 `cta_url` 按钮，不能同时携带 `buttons`、`list` 或 `cards`。请参阅中心的[按钮](/docs/guides/whatsapp/message-types/interactive#buttons)部分，了解共享的按钮结构，轮播卡片自身的链接按钮也复用该结构。

## 标题和页脚

标题是可选的，支持以下四种形式之一：

```text
"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` 按钮变量的[模板](/docs/guides/whatsapp/templates)由 WhatsApp 本身为每个收件人个性化，通过发送请求的 `button` 组件提供。该变量必须位于地址末尾，写作 `{{1}}`，因此它可以改变尾部路径段或查询值，但不能改变主机名或 URL 中间部分。权衡：模板提供按收件人的 URL 和客服窗口外的投递能力，代价是需要 Meta 审核和固定的已批准结构；而 `cta_url` 发送提供自由格式、无需审核的窗口内发送，URL 由你自行变化。

## 限制和边界情况

- **客服窗口必须处于开启状态。** 链接按钮是服务消息，只能在窗口开启期间投递；参见中心的[客服窗口](/docs/guides/whatsapp/message-types#the-customer-service-window)。窗口检查采用开放式失败策略，因此 `202` 并不能证明发送时窗口确实是开启的。
- **`from` 必须是你的工作区拥有的号码。** 省略它或指定一个未连接的发送号码，请求会在创建发送之前被拒绝。
- **URL 在整个发送中是静态的，对每个收件人完全相同。** 此类型没有按收件人的变量。参见[点击会报告什么](#点击会回传什么)了解如何进行点击归因。
- **永远没有点击信号。** 链接按钮的点击不会产生入站消息，也不会产生 webhook 事件。不要构建仅依赖此类型就承诺点击指标的功能。
- **Bird 检查 URL 的格式，而非协议。**`url` 必须是带协议的绝对地址，但 Bird 不要求 `https`，Meta 也未公布协议限制。相比之下，媒体标题的 `url` 文档要求必须是 `https`。
- **WhatsApp 无法获取的媒体头部 URL 会在发送被接受后失败。** WhatsApp 在发送时获取头部资源并缓存 10 分钟；签名 URL 的有效期必须长于发送耗时，不可达的 URL 将异步失败，消息的 `last_error` 上会出现 `media_rejected`。

中心的[错误](/docs/guides/whatsapp/message-types/interactive#errors)表中列出的结构检查均不会在此类型上触发：它们检查的是列表的行、`buttons` 数组或轮播的卡片，而 `cta_url` 消息不包含这三者中的任何一种。结构错误（例如 `text` 标签超过 20 个字符）会返回通用的请求验证错误，而不是那些特定错误码。无法解析的引用会在创建或计费之前使请求失败：当 id 指定的消息不属于此工作区时返回 `404` [`E15071`](/docs/api/errors/E15071)，当 id 指定的消息无法被引用时返回 `422` [`E15072`](/docs/api/errors/E15072)。对于所有 WhatsApp 发送可能遇到的错误（窗口关闭、发送号码缺失或无效、收件人无效），请参阅中心的[错误](/docs/guides/whatsapp/message-types/interactive#errors)和[发送 WhatsApp 消息](/docs/guides/whatsapp/sending-whatsapp)。

## 后续步骤

- [WhatsApp 互动消息](/docs/guides/whatsapp/message-types/interactive)：六种互动类型的共同内容
- [WhatsApp 模板](/docs/guides/whatsapp/templates)：使用 `url` 按钮变量，由 WhatsApp 为每个收件人个性化
- [发送 WhatsApp 消息](/docs/guides/whatsapp/sending-whatsapp)：请求信封、`202` 模型和安全重试

## Related resources

- [Connecting WhatsApp to Bird: from buying a number to a live channel](/learn/whatsapp/connecting-whatsapp-to-bird) (video)
- [What is the 24-hour customer service window on WhatsApp?](/explained/whatsapp/what-is-the-24-hour-customer-service-window) (answer)
- [WhatsApp message builder](/tools/whatsapp-message-builder) (tool)
- [WhatsApp](/products/whatsapp) (product)

[Get an implementation brief](/learn/workspace?topic=whatsapp)
