# WhatsApp 媒体轮播

媒体轮播是一组 2 到 10 张卡片，收件人可以左右滑动浏览，每张卡片有各自的图片或视频、短文本和按钮。当你需要同时展示多个条目（例如几件商品）时，使用轮播而不是逐条发送消息。

## 发送轮播

将 `interactive.type` 设置为 `carousel`，并指定一个消息级别的 `body_text` 和一个包含 2 到 10 个条目的 `cards` 数组：

**TypeScript**

```typescript
const msg = await bird.whatsapp.send({
  to: "+16505551234",
  from: "+13124495648",
  interactive: {
    type: "carousel",
    body_text: "Here are two of our latest arrivals, each under $25:",
    cards: [
      {
        header: { type: "image", url: "https://cdn.example.com/plants/blue-echeveria.jpeg" },
        buttons: [
          {
            type: "cta_url",
            cta_url: { text: "Buy now", url: "https://shop.example.com/blue-echeveria" },
          },
        ],
      },
      {
        header: { type: "image", url: "https://cdn.example.com/plants/zebra-haworthia.jpeg" },
        buttons: [
          {
            type: "cta_url",
            cta_url: { text: "Buy now", url: "https://shop.example.com/zebra-haworthia" },
          },
        ],
      },
    ],
  },
});
console.log(msg.id, msg.status);
```

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

`from` 在每条服务消息中都是必填项：必须是你的工作区拥有的号码，而不是 Bird 托管的号码。完整结构还可以添加卡片自身的文本、第二个快速回复按钮，以及对早期消息的引用：

```json
{
  "to": "+16505551234",
  "from": "+13124495648",
  "in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
  "interactive": {
    "type": "carousel",
    "body_text": "Here are two of our latest arrivals, each under $25:",
    "cards": [
      {
        "header": { "type": "image", "url": "https://cdn.example.com/plants/blue-echeveria.jpeg" },
        "body_text": "Blue Echeveria. Powdery blue leaves.",
        "buttons": [
          { "type": "quick_reply", "quick_reply": { "slug": "buy-echeveria", "text": "Buy" } },
          { "type": "quick_reply", "quick_reply": { "slug": "info-echeveria", "text": "Details" } }
        ]
      },
      {
        "header": { "type": "image", "url": "https://cdn.example.com/plants/zebra-haworthia.jpeg" },
        "body_text": "Zebra Haworthia. White stripes on deep green leaves.",
        "buttons": [
          { "type": "quick_reply", "quick_reply": { "slug": "buy-haworthia", "text": "Buy" } },
          { "type": "quick_reply", "quick_reply": { "slug": "info-haworthia", "text": "Details" } }
        ]
      }
    ]
  },
  "tags": [{ "name": "category", "value": "catalog" }],
  "metadata": { "order_id": "A-1" }
}
```

`in_reply_to_message_id` 引用同一会话中的早期消息。请参阅 Hub 的[引用消息以关联回复](/docs/guides/whatsapp/message-types/interactive#quoting-a-message-to-correlate-a-reply)，了解解析机制及其可能遗漏的情况。

轮播不接受消息级别的请求头和页脚：消息的 `body_text` 是卡片上方唯一的文本。请参阅 Hub 的[按钮](/docs/guides/whatsapp/message-types/interactive#buttons)部分，了解该类型的卡片复用的共享按钮结构。

## 卡片

每张卡片有各自的媒体请求头、短文本和按钮：

- **`header`** 在每张卡片上都是必填项，且只能为 `image` 或 `video`：不支持文本和文档请求头，与其他交互类型不同。
- **`body_text`** 是可选的。它位于卡片媒体下方，长度上限比消息正文短，最多允许两个换行符。
- **`buttons`** 是必填项：一个 `cta_url` 按钮，或最多三个 `quick_reply` 按钮，同一张卡片上不可混用。

卡片按照在 `cards` 数组中出现的顺序从左到右渲染。卡片没有页脚，也没有自身的索引字段；它在数组中的位置就是它在轮播中的位置。

## 每张卡片携带相同的按钮

轮播中的每张卡片必须携带**相同的按钮类型、相同的数量、相同的顺序**。如果卡片 1 有一个 `cta_url` 按钮而卡片 2 有两个 `quick_reply` 按钮，轮播会被拒绝；如果每张卡片都有两个 `quick_reply` 按钮但顺序不同，同样会被拒绝。

原因在于 WhatsApp 渲染消息的方式：轮播是一个具有共享布局的单卡片视图，而非一组独立布局的卡片。不同的按钮行会破坏共享布局，因此 WhatsApp 要求每张卡片保持一致，Bird 会在创建或计费发送之前进行检查。不匹配时返回 [E15059](/docs/api/errors/E15059)。

按钮标签有单独的规则，其作用范围不同：标签必须在**单张卡片内**唯一，而非在整个轮播中唯一。十张卡片上都使用 "Buy now" 没有问题；同一张卡片上出现两次 "Buy now" 则返回 [E15056](/docs/api/errors/E15056)。

## 限制

| 字段                                           | 限制                                                              |
| ---------------------------------------------- | ----------------------------------------------------------------- |
| `cards`                                        | 2 到 10 个条目                                                    |
| 卡片 `header`                                  | 每张卡片必填；仅限 `image` 或 `video`                             |
| 卡片 `header.url`                              | 必填，无最大长度限制                                              |
| 卡片 `body_text`                               | 可选，1 到 160 个字符，最多 2 个换行符                            |
| 卡片 `buttons`                                 | 1 到 3 个条目：一个 `cta_url`，或最多三个 `quick_reply`，不可混用 |
| 按钮标签（`quick_reply.text`、`cta_url.text`） | 必填，1 到 20 个字符，在卡片内唯一                                |
| `quick_reply.slug`                             | 必填，1 到 256 个字符                                             |
| `cta_url.url`                                  | 必填，1 到 2000 个字符                                            |
| 消息 `body_text`                               | 必填，1 到 1024 个字符                                            |
| 消息请求头、页脚                               | 轮播中不允许：无 `header`，无 `footer_text`                       |

Bird 将每张卡片的 `quick_reply` 按钮上限设为三个。Meta 本身未声明数量限制，仅说明卡片接受一个链接按钮或一个或多个回复按钮，因此这一上限是 Bird 自身的，而非 WhatsApp 的。

## 读取回复

只有 `quick_reply` 卡片按钮会产生回复。点击后，回复作为独立的入站消息到达，携带 `interactive_reply`：

```json
{
  "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": "buy-echeveria",
      "text": "Buy"
    }
  },
  "created_at": "2026-08-25T09:04:11Z"
}
```

你在被点击按钮上设置的 `slug` 会原样返回到 `interactive_reply.button.slug` 上，与回复按钮点击产生的结构相同。你可以通过消息列表或 `GET /v1/whatsapp/messages/{id}` 查看此回复；请参阅 Hub 的[读取回复](/docs/guides/whatsapp/message-types/interactive#reading-a-reply)了解完整路径。

卡片上的 `cta_url` 按钮会在收件人的浏览器中打开链接，不会发送任何回传，与独立的[链接按钮](/docs/guides/whatsapp/message-types/interactive/cta-url-buttons)相同。

## 自由格式轮播与模板轮播

本页介绍的是通过 `interactive.type: "carousel"` 内联发送的自由格式轮播，它只能在客户服务窗口打开时投递，且无需经过 Meta 审核。[WhatsApp 模板](/docs/guides/whatsapp/templates)有自己独立的轮播：模板组件只需创建一次，提交给 Meta 审批，然后像其他模板一样通过 slug 发送，包括在窗口之外。两者共享 "carousel" 一词和 Meta 的 2 到 10 张卡片范围，除此之外没有任何相同之处：不同的传输格式、不同的审核路径，且模板轮播的卡片数量在模板审批时固定，而非每次发送时选择。如果你在浏览模板时看到 "carousel"，那是模板类型，不是本页内容。

## 限制与边界情况

- **客户服务窗口必须处于打开状态。** 轮播是一条服务消息，只能在窗口打开时投递；请参阅 Hub 的[客户服务窗口](/docs/guides/whatsapp/message-types#the-customer-service-window)。窗口检查采用开放式失败策略，因此 `202` 并不能证明发送时窗口确实是打开的。
- **`from` 必须是你的工作区拥有的号码。** 省略该字段或指定一个非已连接发送方的号码，会在创建发送之前被拒绝。
- **卡片媒体在发送调度时必须可公开访问。** Bird 不存储也不代理文件：WhatsApp 在发送时自行获取每张卡片的 `url`，因此签名 URL 的有效期必须长于发送耗时。
- **WhatsApp 无法获取的卡片媒体 URL 会被接受，然后异步失败，且仍会被计费。** Bird 的请求验证仅检查卡片的 `url` 是否为格式正确的 URI，而不检查 WhatsApp 能否访问它或它是否使用 `https`。超大文件、404、无法解析的主机或错误的文件类型都会在接受时返回 `202`，然后 `whatsapp.accepted`，然后 `whatsapp.sent`，然后 `whatsapp.failed`，消息的 `last_error` 上会显示 `media_rejected`，且发送费用已扣除且无退款途径。发送前请测试每张卡片的 URL，因为损坏的 URL 只有在事后才会被发现。
- **每张卡片必须携带相同的按钮。** 请参阅上方的[每张卡片携带相同的按钮](#每张卡片携带相同的按钮)；这是请求 schema 无法独立表达的唯一轮播规则，因此会单独检查并返回 [E15059](/docs/api/errors/E15059)，而非通用验证错误。
- **无消息级别的请求头或页脚。** 轮播中卡片上方的唯一文本是 `body_text`；不像其他类型那样有地方使用 `footer_text` 放置附加说明。
- **回复不携带卡片索引。** 卡片的 `quick_reply` 点击仅报告 `{slug, text}`，与回复按钮点击的结构相同，没有字段标明来自哪张卡片。如果你需要知道被点击的是哪张卡片，请在每个按钮的 `slug` 中编码卡片信息，例如使用 `buy-echeveria` 而非单独的 `buy`。
- **`cta_url` 卡片按钮不会生成入站事件。** 如果你需要了解卡片是否被互动过，请在该卡片上使用 `quick_reply` 按钮，或在你自己的目标 URL 上追踪点击。

除 E15059 外，轮播特有的唯一交互错误是 [E15056](/docs/api/errors/E15056)，表示同一张卡片上出现了重复的按钮标签。无法解析的引用会在创建或计费之前使请求失败：当 id 指定了该工作区中不存在的消息时返回 `404` [`E15071`](/docs/api/errors/E15071)，当 id 指定了无法被引用的消息时返回 `422` [`E15072`](/docs/api/errors/E15072)。关于任何 WhatsApp 发送都可能遇到的错误（窗口已关闭、发送方缺失或无效、收件人无效），请参阅 Hub 的[错误](/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)：用于在客户服务窗口之外发送轮播
- [发送 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)
