Karuzele multimedialne WhatsApp
Karuzela multimedialna to zestaw od dwóch do dziesięciu kart, które odbiorca przewija obok siebie, każda z własnym obrazem lub wideo, własnym krótkim tekstem i własnymi przyciskami. Użyj jej, żeby pokazać kilka elementów naraz, na przykład garść produktów, zamiast wysyłać osobną wiadomość na każdy element.
Wyślij karuzelę
Ustaw interactive.type na carousel, z body_text na poziomie wiadomości i tablicą cards zawierającą od 2 do 10 elementów:
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);msg = client.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"}}],
},
],
},
)
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: "carousel",
BodyText: "Here are two of our latest arrivals, each under $25:",
Cards: &[]bird.WhatsAppInteractiveCardSend{
{
Header: bird.WhatsAppInteractiveCardHeaderSend{Type: "image", Url: "https://cdn.example.com/plants/blue-echeveria.jpeg"},
Buttons: []bird.WhatsAppInteractiveButtonSend{{Type: "cta_url", CtaUrl: &bird.WhatsAppInteractiveCtaUrlSend{Text: "Buy now", Url: "https://shop.example.com/blue-echeveria"}}},
},
{
Header: bird.WhatsAppInteractiveCardHeaderSend{Type: "image", Url: "https://cdn.example.com/plants/zebra-haworthia.jpeg"},
Buttons: []bird.WhatsAppInteractiveButtonSend{{Type: "cta_url", CtaUrl: &bird.WhatsAppInteractiveCtaUrlSend{Text: "Buy now", Url: "https://shop.example.com/zebra-haworthia"}}},
},
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$interactive = (new WhatsAppMessageSendRequestInteractive())
->setType('carousel')
->setBodyText('Here are two of our latest arrivals, each under $25:')
->setCards([
(new WhatsAppInteractiveCardSend())
->setHeader((new WhatsAppInteractiveCardSendHeader())->setType('image')->setUrl('https://cdn.example.com/plants/blue-echeveria.jpeg'))
->setButtons([
(new WhatsAppInteractiveButtonSend())
->setType('cta_url')
->setCtaUrl((new WhatsAppInteractiveButtonSendCtaUrl())->setText('Buy now')->setUrl('https://shop.example.com/blue-echeveria')),
]),
(new WhatsAppInteractiveCardSend())
->setHeader((new WhatsAppInteractiveCardSendHeader())->setType('image')->setUrl('https://cdn.example.com/plants/zebra-haworthia.jpeg'))
->setButtons([
(new WhatsAppInteractiveButtonSend())
->setType('cta_url')
->setCtaUrl((new WhatsAppInteractiveButtonSendCtaUrl())->setText('Buy now')->setUrl('https://shop.example.com/zebra-haworthia')),
]),
]);
$message = $bird->whatsapp->send(
to: '+16505551234',
from: '+13124495648',
interactive: $interactive,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--from +13124495648 \
--interactive '{"body_text":"Here are two of our latest arrivals, each under $25:","cards":[{"buttons":[{"cta_url":{"text":"Buy now","url":"https://shop.example.com/blue-echeveria"},"type":"cta_url"}],"header":{"type":"image","url":"https://cdn.example.com/plants/blue-echeveria.jpeg"}},{"buttons":[{"cta_url":{"text":"Buy now","url":"https://shop.example.com/zebra-haworthia"},"type":"cta_url"}],"header":{"type":"image","url":"https://cdn.example.com/plants/zebra-haworthia.jpeg"}}],"type":"carousel"}' \
--to +16505551234{
"name": "whatsapp_send",
"arguments": {
"from": "+13124495648",
"interactive": {
"body_text": "Here are two of our latest arrivals, each under $25:",
"cards": [
{
"buttons": [
{
"cta_url": {
"text": "Buy now",
"url": "https://shop.example.com/blue-echeveria"
},
"type": "cta_url"
}
],
"header": {
"type": "image",
"url": "https://cdn.example.com/plants/blue-echeveria.jpeg"
}
},
{
"buttons": [
{
"cta_url": {
"text": "Buy now",
"url": "https://shop.example.com/zebra-haworthia"
},
"type": "cta_url"
}
],
"header": {
"type": "image",
"url": "https://cdn.example.com/plants/zebra-haworthia.jpeg"
}
}
],
"type": "carousel"
},
"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": "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" } }]
}
]
}
}'from jest wymagane w każdej wiadomości serwisowej: numer należący do Twojego obszaru roboczego, nie zarządzany przez Bird. Pełna struktura dodaje własny tekst karty, drugi przycisk szybkiej odpowiedzi i cytat wcześniejszej wiadomości:
Przykład kodu
{
"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 cytuje wcześniejszą wiadomość w tej samej konwersacji. Zobacz w hubie sekcję cytowanie wiadomości w celu powiązania odpowiedzi, żeby dowiedzieć się, jak działa rozwiązywanie i czego może nie objąć.
Karuzela nie ma nagłówka ani stopki na poziomie wiadomości: body_text wiadomości to jedyny tekst nad kartami. Zobacz w hubie sekcję przyciski, żeby poznać wspólną strukturę przycisków wykorzystywaną przez karty tego typu.
Karty
Każda karta ma własny nagłówek multimedialny, własny krótki tekst i własne przyciski:
- header jest wymagane na każdej karcie i przyjmuje wyłącznie image lub video: bez tekstu i bez nagłówka dokumentu, w odróżnieniu od pozostałych typów interaktywnych.
- body_text jest opcjonalne. Wyświetla się pod multimediami karty, jest krótsze niż treść wiadomości i dopuszcza najwyżej dwa podziały wiersza.
- buttons jest wymagane: albo jeden przycisk cta_url, albo do trzech przycisków quick_reply, nigdy mieszanka na jednej karcie.
Karty renderują się od lewej do prawej w kolejności, w jakiej pojawiają się w tablicy cards. Karta nie ma stopki ani własnego pola indeksu; jej pozycja w tablicy to jej pozycja w karuzeli.
Każda karta ma te same przyciski
Każda karta w karuzeli musi mieć te same typy przycisków, tę samą ich liczbę i w tej samej kolejności. Karuzela, w której karta 1 ma jeden przycisk cta_url, a karta 2 dwa przyciski quick_reply, zostanie odrzucona. To samo dotyczy karuzeli, w której każda karta ma dwa przyciski quick_reply, ale w innej kolejności.
Powodem jest sposób, w jaki WhatsApp renderuje wiadomość: karuzela to jeden widok karty ze wspólnym układem, a nie zestaw niezależnie rozmieszczonych kart. Karta z innym wierszem przycisków złamałaby ten wspólny układ, dlatego WhatsApp wymaga, żeby każda karta była zgodna, a Bird sprawdza to przed utworzeniem lub naliczeniem wysyłki. Niezgodność zwraca E15059.
Etykiety przycisków to osobna reguła, o innym zakresie: etykieta musi być unikalna w obrębie karty, a nie w całej karuzeli. "Buy now" na każdej z dziesięciu kart jest dopuszczalne; "Buy now" dwukrotnie na tej samej karcie zwraca E15056.
Limity
| Pole | Ograniczenie |
|---|---|
| cards | od 2 do 10 elementów |
| Karta header | wymagane na każdej karcie; wyłącznie image lub video |
| Karta header.url | wymagane, brak maksymalnej długości |
| Karta body_text | opcjonalne, od 1 do 160 znaków, najwyżej 2 podziały wiersza |
| Karta buttons | od 1 do 3 elementów: jeden cta_url lub do trzech quick_reply, nigdy mieszane |
| Etykieta przycisku (quick_reply.text, cta_url.text) | wymagane, od 1 do 20 znaków, unikalne w obrębie karty |
| quick_reply.slug | wymagane, od 1 do 256 znaków |
| cta_url.url | wymagane, od 1 do 2000 znaków |
| Wiadomość body_text | wymagane, od 1 do 1024 znaków |
| Nagłówek wiadomości, stopka | niedozwolone w karuzeli: brak header, brak footer_text |
Bird ogranicza przyciski quick_reply do trzech na kartę. Samo Meta nie podaje limitu liczbowego, a jedynie że karta przyjmuje albo jeden przycisk linkowy, albo jeden lub więcej przycisków odpowiedzi, więc ten pułap należy do Bird, a nie do WhatsApp.
Odczytywanie odpowiedzi
Tylko przycisk quick_reply na karcie generuje odpowiedź. Jego dotknięcie dociera jako osobna wiadomość przychodząca, zawierająca interactive_reply:
Przykład kodu
{
"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 ustawione na dotkniętym przycisku wraca bez zmian w interactive_reply.button.slug, ta sama struktura, którą zwraca dotknięcie przycisków odpowiedzi. Tę odpowiedź zobaczysz na liście wiadomości lub w GET /v1/whatsapp/messages/{id}; zobacz w hubie sekcję odczytywanie odpowiedzi, żeby poznać tę ścieżkę w całości.
Przycisk cta_url na karcie otwiera link w przeglądarce odbiorcy i nie odsyła nic z powrotem, tak samo jak samodzielny przycisk linkowy.
Karuzele swobodne i karuzele szablonowe
Ta strona opisuje karuzelę swobodną, którą wysyłasz inline za pomocą interactive.type: "carousel", dostarczalną tylko w otwartym oknie obsługi klienta i nigdy nierewidowaną przez Meta. Szablony WhatsApp mają własną, osobną karuzelę: komponent szablonu tworzony raz, przesyłany do Meta do zatwierdzenia i wysyłany po slugu jak każdy inny szablon, również poza oknem. Oba typy dzielą słowo "carousel" i zakres 2–10 kart Meta, i nic więcej: różne struktury przesyłu, różne ścieżki przeglądu, a liczba kart karuzeli szablonowej jest ustalona w momencie zatwierdzenia szablonu, a nie wybierana przy każdej wysyłce. Jeśli przeglądasz szablony i widzisz tam "carousel", to typ szablonu, nie ta strona.
Limity i przypadki brzegowe
- Okno obsługi klienta musi być otwarte. Karuzela to wiadomość serwisowa, dostarczalna tylko w otwartym oknie; zobacz w hubie sekcję okno obsługi klienta. Sprawdzanie okna nie blokuje wysyłki przy niepowodzeniu, więc 202 nie jest dowodem, że okno było faktycznie otwarte w momencie wysyłki.
- from musi być numerem należącym do Twojego obszaru roboczego. Pominięcie go lub podanie numeru, który nie jest podłączonym nadawcą, jest odrzucane przed utworzeniem wysyłki.
- Multimedia karty muszą być publicznie dostępne w momencie realizacji wysyłki. Bird nie przechowuje ani nie pośredniczy w dostępie do pliku: WhatsApp pobiera url każdej karty samodzielnie w momencie wysyłki, więc podpisany URL musi być ważny dłużej niż trwa wysyłka.
- URL multimediów karty, którego WhatsApp nie może pobrać, zostaje zaakceptowany, potem kończy się niepowodzeniem asynchronicznie i nadal jest naliczany. Walidacja żądania Bird sprawdza jedynie, czy url karty jest poprawnie sformowanym URI, a nie czy WhatsApp może go osiągnąć ani czy używa https. Za duży plik, 404, nierozwiązywalny host lub nieprawidłowy typ pliku: wszystko to wraca jako 202 przy akceptacji, potem whatsapp.accepted, potem whatsapp.sent, potem whatsapp.failed, z media_rejected w last_error wiadomości, a koszt wysyłki jest już naliczony bez możliwości zwrotu. Przetestuj URL każdej karty przed wysyłką, ponieważ uszkodzony URL nie zostanie wykryty aż po fakcie.
- Każda karta musi mieć te same przyciski. Zobacz Każda karta ma te same przyciski powyżej; to jedyna reguła karuzeli, której schemat żądania nie potrafi wyrazić samodzielnie, dlatego jest sprawdzana osobno i zwraca E15059 zamiast ogólnego błędu walidacji.
- Brak nagłówka ani stopki na poziomie wiadomości. Jedynym tekstem karuzeli nad kartami jest body_text; nie ma gdzie umieścić drobnego druku tak, jak robią to inne typy za pomocą footer_text.
- Odpowiedź nie zawiera indeksu karty. Dotknięcie quick_reply na karcie raportuje tylko {slug, text}, tę samą strukturę co dotknięcie przycisków odpowiedzi, bez pola wskazującego, z której karty pochodzi. Jeśli musisz wiedzieć, którą kartę dotknięto, zakoduj kartę w slug każdego przycisku, na przykład buy-echeveria zamiast samego buy.
- Przycisk cta_url na karcie nie generuje zdarzenia przychodzącego. Jeśli musisz wiedzieć, że karta została użyta, zastosuj na niej przyciski quick_reply albo śledź kliknięcie na własnym docelowym URL.
Poza E15059 jedynym błędem interaktywnym specyficznym dla karuzeli jest E15056 za powtórzoną etykietę przycisku na jednej karcie. Cytowanie, które nie zostanie rozwiązane, kończy żądanie niepowodzeniem przed utworzeniem lub naliczeniem czegokolwiek: 404 E15071, gdy id wskazuje wiadomość, której ten obszar roboczy nie posiada, 422 E15072, gdy wskazuje wiadomość, której nie można zacytować. Informacje o błędach, na które może natrafić każda wysyłka WhatsApp (zamknięte okno, brakujący lub nieprawidłowy nadawca, nieprawidłowy odbiorca), znajdziesz w hubie w sekcjach błędy i Wysyłanie wiadomości WhatsApp.
Następne kroki
- Wiadomości interaktywne WhatsApp: co łączy wszystkie sześć typów interaktywnych
- Szablony WhatsApp: karuzela wysyłana poza oknem obsługi klienta
- Wysyłanie wiadomości WhatsApp: koperta żądania, model 202 i bezpieczne ponawianie
Powiązane zasoby
Kontynuuj z dokumentacją, przewodnikami i przykładami dotyczącymi tego tematu. Zasoby są w języku angielskim.
Obejrzyj przewodnikConnecting WhatsApp to Bird: from buying a number to a live channelZrozum koncepcjęWhat is the 24-hour customer service window on WhatsApp?Użyj narzędziaWhatsApp message builderPoznaj możliwościWhatsApp
Wypróbuj ćwiczenie i uzyskaj brief wdrożeniowy