Messages interactifs WhatsApp
Un message interactif est un corps de texte accompagné d'un élément sur lequel le destinataire peut appuyer : un bouton WhatsApp, un menu, un lien, une carte, ou une demande de localisation ou de coordonnées. Là où une réponse par modèle oblige à analyser du texte libre, un menu WhatsApp ou un ensemble de boutons WhatsApp offre au destinataire un choix fixe et vous renvoie une valeur que vous avez définie. Cette page couvre ce que les six types ont en commun ; la page de chaque type décrit sa structure réseau et ses propres limites.
Les six types
| Type | Bird interactive.type | En-tête | Pied | Corps max |
|---|---|---|---|---|
| Boutons de réponse | button | texte, image, vidéo, document | oui | 1024 |
| Menus à liste | list | texte uniquement | oui | 4096 |
| Boutons de lien | cta_url | texte, image, vidéo, document | oui | 1024 |
| Carrousels média | carousel | aucun sur le message ; image ou vidéo par carte | non | 1024 message, 160 par carte |
| Demandes de localisation | location_request_message | aucun | non | 1024 |
| Demandes de coordonnées | request_contact_info | aucun | non | 1024 |
Chaque type est libre : il ne peut être envoyé que dans une fenêtre de service client ouverte, et n'est jamais soumis à l'examen de Meta comme l'est un modèle.
Les messages interactifs sont du contenu libre, donc la règle de la fenêtre de service client s'applique : consultez la fenêtre de service client pour comprendre ce que cela signifie et ce qu'une fenêtre fermée renvoie.
Chaque envoi interactif requiert aussi from, un numéro que votre espace de travail possède. Les numéros gérés par Bird ne le prennent pas en charge ; un envoi interactif nécessite donc qu'un numéro qui vous appartient soit d'abord connecté.
Le champ de contenu interactif
interactive est l'un des champs de contenu mutuellement exclusifs de POST /v1/whatsapp/messages, aux côtés de template, text, image et des autres : un seul peut être présent par envoi. Dans interactive, type indique laquelle des six variantes est utilisée, et le champ propre à cette variante contient le reste (buttons, list, cta_url ou cards). Le schéma interdit le champ de toute autre variante ; combiner deux variantes sur un même envoi échoue à la validation avant d'atteindre un handler.
Pour l'enveloppe de requête, le modèle de réponse 202 et les réessais sûrs, consultez Envoyer des messages WhatsApp plutôt que cette page.
Voici un message interactif minimal : deux boutons WhatsApp sur un envoi de type boutons de réponse, une langue à la fois.
const msg = await bird.whatsapp.send({
to: "+15551234567",
from: "+13124495648",
interactive: {
type: "button",
body_text: "Your gardening workshop is scheduled for 9am tomorrow.",
buttons: [
{ type: "quick_reply", quick_reply: { slug: "change-booking", text: "Change" } },
{ type: "quick_reply", quick_reply: { slug: "cancel-booking", text: "Cancel" } },
],
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+15551234567",
from_="+13124495648",
interactive={
"type": "button",
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"buttons": [
{"type": "quick_reply", "quick_reply": {"slug": "change-booking", "text": "Change"}},
{"type": "quick_reply", "quick_reply": {"slug": "cancel-booking", "text": "Cancel"}},
],
},
)
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: "+15551234567",
From: "+13124495648",
Interactive: &bird.WhatsAppInteractiveSend{
Type: "button",
BodyText: "Your gardening workshop is scheduled for 9am tomorrow.",
Buttons: &[]bird.WhatsAppInteractiveButtonSend{
{Type: "quick_reply", QuickReply: &bird.WhatsAppInteractiveQuickReplyButtonSend{Slug: "change-booking", Text: "Change"}},
{Type: "quick_reply", QuickReply: &bird.WhatsAppInteractiveQuickReplyButtonSend{Slug: "cancel-booking", Text: "Cancel"}},
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$interactive = (new WhatsAppMessageSendRequestInteractive())
->setType('button')
->setBodyText('Your gardening workshop is scheduled for 9am tomorrow.')
->setButtons([
(new WhatsAppInteractiveButtonSend())
->setType('quick_reply')
->setQuickReply((new WhatsAppInteractiveButtonSendQuickReply())->setSlug('change-booking')->setText('Change')),
(new WhatsAppInteractiveButtonSend())
->setType('quick_reply')
->setQuickReply((new WhatsAppInteractiveButtonSendQuickReply())->setSlug('cancel-booking')->setText('Cancel')),
]);
$message = $bird->whatsapp->send(
to: '+15551234567',
from: '+13124495648',
interactive: $interactive,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--from +13124495648 \
--interactive '{"body_text":"Your gardening workshop is scheduled for 9am tomorrow.","buttons":[{"quick_reply":{"slug":"change-booking","text":"Change"},"type":"quick_reply"},{"quick_reply":{"slug":"cancel-booking","text":"Cancel"},"type":"quick_reply"}],"type":"button"}' \
--to +15551234567{
"name": "whatsapp_send",
"arguments": {
"from": "+13124495648",
"interactive": {
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"buttons": [
{
"quick_reply": {
"slug": "change-booking",
"text": "Change"
},
"type": "quick_reply"
},
{
"quick_reply": {
"slug": "cancel-booking",
"text": "Cancel"
},
"type": "quick_reply"
}
],
"type": "button"
},
"to": "+15551234567"
}
}curl -X POST "https://{region}.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "+15551234567",
"from": "+13124495648",
"interactive": {
"type": "button",
"body_text": "Your gardening workshop is scheduled for 9am tomorrow.",
"buttons": [
{ "type": "quick_reply", "quick_reply": { "slug": "change-booking", "text": "Change" } },
{ "type": "quick_reply", "quick_reply": { "slug": "cancel-booking", "text": "Cancel" } }
]
}
}'Boutons
Quatre des six types placent un bouton, et tous s'appuient sur la même forme : un objet discriminé dont type est quick_reply ou cta_url, chacun portant son propre champ imbriqué du même nom. Un bouton quick_reply porte slug et text ; un bouton cta_url porte text et url. Quels types acceptent quelle forme de bouton :
- Les boutons de réponse n'envoient que des boutons quick_reply, de 1 à 3.
- Les boutons de lien envoient exactement un bouton cta_url.
- Les carrousels média placent des boutons sur chaque carte : soit un bouton cta_url, soit jusqu'à trois boutons quick_reply, et toutes les cartes du carrousel doivent être cohérentes.
- Les menus à liste utilisent des lignes dans des sections plutôt que cet objet bouton ; ils sont traités sur leur propre page.
Le slug d'un bouton quick_reply est votre propre identifiant pour ce bouton. Il n'est jamais affiché au destinataire, seul son libellé text l'est, et le slug est renvoyé tel quel dans la réponse. C'est cet aller-retour qui permet de corréler une réponse au bouton qui l'a produite ; il vaut donc la peine de le dire une fois ici, plutôt que sur chaque sous-page.
Lire une réponse
Appuyer sur un bouton ou choisir une ligne de menu envoie son propre message entrant, contenant un objet interactive_reply. interactive_reply.type vaut button ou list ; dans les deux cas, l'objet imbriqué porte le slug et le text que vous avez déclarés, le libellé sur lequel le destinataire a réellement appuyé. Les deux types de demande, localisation et coordonnées, répondent différemment : la réponse à une demande de localisation est un message entrant location ordinaire, et la réponse à une demande de coordonnées est une fiche contact entrante, pas un interactive_reply.
Une réponse vous parvient via la liste de messages et GET /v1/whatsapp/messages/{id}, de la même manière que tout message entrant WhatsApp. Pour agir dès l'arrivée plutôt que par interrogation, abonnez-vous au webhook whatsapp.received : son payload porte interactive_reply, il nomme donc déjà le bouton ou la ligne sur lequel l'utilisateur a appuyé. Recevoir des réponses interactives couvre la forme de lecture d'un appui, le payload du webhook et les appuis qui arrivent sur un autre champ.
Citer un message pour corréler une réponse
in_reply_to_message_id sur un envoi cite un message antérieur de la même conversation, et chaque message, envoyé ou reçu, le renvoie en lecture. C'est un seul champ pour les deux directions.
La corrélation que cela vous offre est asymétrique. Un appui sur un bouton WhatsApp ou une ligne de menu porte le context propre à Meta, donc in_reply_to_message_id se résout vers le message qui l'a proposé. Une fiche contact partagée ne porte aucun context, donc elle ne se résout vers rien : vous corrélez la réponse d'une demande de coordonnées par from et le timing, pas par ce champ.
La résolution passe par un store de contexte de message, et un échec de résolution omet le champ au lieu d'en rapporter un. Sur le réseau, c'est indiscernable d'une réponse qui ne répond à rien. Une intégration qui a besoin d'une corrélation fiable ne doit pas se fier à ce seul champ : portez votre propre metadata sur l'envoi et faites la correspondance sur celui-ci.
La fenêtre pendant laquelle un message reste citable est limitée à 15 jours ; au-delà, l'envoi échoue avec une 404 E15071, car Bird ne détient plus l'identifiant fournisseur nécessaire à la citation. Envoyer des messages WhatsApp gère le champ côté envoi : sa longueur, sa résolution et la forme de la requête.
Erreurs
Trois codes d'erreur sont propres au contenu interactif. Chacun ne se déclenche que sur les types possédant le champ qu'il vérifie ; la quatrième colonne indique donc quels types peuvent réellement le produire.
| Code | Statut | Déclencheur | S'applique à |
|---|---|---|---|
| E15055 WhatsAppInteractiveLimitExceeded | 422 | Le message dépasse une limite pour son type ; plus de 10 lignes dans les sections d'une liste. | Menus à liste uniquement |
| E15056 WhatsAppInteractiveDuplicateLabel | 422 | Deux boutons ou lignes du même message partagent un libellé. | Tout type avec des boutons ou lignes libellés : boutons de réponse, menus à liste, carrousels média |
| E15059 WhatsAppInteractiveCarouselButtonsMismatch | 422 | Les cartes d'un carrousel ne portent pas toutes les mêmes boutons. | Carrousels média uniquement |
Chaque envoi interactif peut aussi déclencher les erreurs communes à tout envoi WhatsApp : fenêtre de service client fermée, expéditeur manquant ou invalide, destinataire invalide ou contenu ambigu. Elles sont partagées entre tous les types de contenu WhatsApp, et ne sont pas propres aux messages interactifs ; consultez Envoyer des messages WhatsApp pour cette liste plutôt qu'une copie ici.
Étapes suivantes
- Envoyer des messages WhatsApp : l'enveloppe de requête, le modèle 202 et les réessais sûrs
- Événements WhatsApp : suivre la livraison par message, via le API ou les webhooks
- Modèles WhatsApp : les messages que vous pouvez encore envoyer une fois la fenêtre fermée
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet. Les ressources sont en anglais.
Regarder le guideConnecting WhatsApp to Bird: from buying a number to a live channelComprendre le conceptWhat is the 24-hour customer service window on WhatsApp?Utiliser l'outilWhatsApp message builderExplorer la fonctionnalitéWhatsApp
Essayez la pratique et obtenez un guide d'implémentation