Sign inGet started

WhatsApp 媒体轮播

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

发送轮播

interactive.type 设置为 carousel,并指定一个消息级别的 body_text 和一个包含 2 到 10 个条目的 cards 数组:
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);
from 在每条服务消息中都是必填项:必须是你的工作区拥有的号码,而不是 Bird 托管的号码。完整结构还可以添加卡片自身的文本、第二个快速回复按钮,以及对早期消息的引用:
代码示例
{
  "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 的引用消息以关联回复,了解解析机制及其可能遗漏的情况。
轮播不接受消息级别的请求头和页脚:消息的 body_text 是卡片上方唯一的文本。请参阅 Hub 的按钮部分,了解该类型的卡片复用的共享按钮结构。

卡片

每张卡片有各自的媒体请求头、短文本和按钮:
  • header 在每张卡片上都是必填项,且只能为 imagevideo:不支持文本和文档请求头,与其他交互类型不同。
  • body_text 是可选的。它位于卡片媒体下方,长度上限比消息正文短,最多允许两个换行符。
  • buttons 是必填项:一个 cta_url 按钮,或最多三个 quick_reply 按钮,同一张卡片上不可混用。
卡片按照在 cards 数组中出现的顺序从左到右渲染。卡片没有页脚,也没有自身的索引字段;它在数组中的位置就是它在轮播中的位置。

每张卡片携带相同的按钮

轮播中的每张卡片必须携带相同的按钮类型、相同的数量、相同的顺序。如果卡片 1 有一个 cta_url 按钮而卡片 2 有两个 quick_reply 按钮,轮播会被拒绝;如果每张卡片都有两个 quick_reply 按钮但顺序不同,同样会被拒绝。
原因在于 WhatsApp 渲染消息的方式:轮播是一个具有共享布局的单卡片视图,而非一组独立布局的卡片。不同的按钮行会破坏共享布局,因此 WhatsApp 要求每张卡片保持一致,Bird 会在创建或计费发送之前进行检查。不匹配时返回 E15059
按钮标签有单独的规则,其作用范围不同:标签必须在单张卡片内唯一,而非在整个轮播中唯一。十张卡片上都使用 "Buy now" 没有问题;同一张卡片上出现两次 "Buy now" 则返回 E15056

限制

字段限制
cards2 到 10 个条目
卡片 header每张卡片必填;仅限 imagevideo
卡片 header.url必填,无最大长度限制
卡片 body_text可选,1 到 160 个字符,最多 2 个换行符
卡片 buttons1 到 3 个条目:一个 cta_url,或最多三个 quick_reply,不可混用
按钮标签(quick_reply.textcta_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
代码示例
{
  "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 的读取回复了解完整路径。
卡片上的 cta_url 按钮会在收件人的浏览器中打开链接,不会发送任何回传,与独立的链接按钮相同。

自由格式轮播与模板轮播

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

限制与边界情况

  • 客户服务窗口必须处于打开状态。 轮播是一条服务消息,只能在窗口打开时投递;请参阅 Hub 的客户服务窗口。窗口检查采用开放式失败策略,因此 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,而非通用验证错误。
  • 无消息级别的请求头或页脚。 轮播中卡片上方的唯一文本是 body_text;不像其他类型那样有地方使用 footer_text 放置附加说明。
  • 回复不携带卡片索引。 卡片的 quick_reply 点击仅报告 {slug, text},与回复按钮点击的结构相同,没有字段标明来自哪张卡片。如果你需要知道被点击的是哪张卡片,请在每个按钮的 slug 中编码卡片信息,例如使用 buy-echeveria 而非单独的 buy
  • cta_url 卡片按钮不会生成入站事件。 如果你需要了解卡片是否被互动过,请在该卡片上使用 quick_reply 按钮,或在你自己的目标 URL 上追踪点击。
除 E15059 外,轮播特有的唯一交互错误是 E15056,表示同一张卡片上出现了重复的按钮标签。无法解析的引用会在创建或计费之前使请求失败:当 id 指定了该工作区中不存在的消息时返回 404 E15071,当 id 指定了无法被引用的消息时返回 422 E15072。关于任何 WhatsApp 发送都可能遇到的错误(窗口已关闭、发送方缺失或无效、收件人无效),请参阅 Hub 的错误发送 WhatsApp 消息

后续步骤