Aller au contenu
23 min read

Connecter votre logiciel à WhatsApp

Délivrez une clé API, associez un modèle WhatsApp approuvé, dirigez un webhook vers votre propre serveur et appelez l’API REST — le guide complet de la gestion des API.

Connecter votre logiciel à WhatsApp

La gestion des API, c’est la façon dont un autre système — votre logiciel de facturation, votre boutique en ligne, votre propre backend — envoie des messages WhatsApp via WizMessage. Vous lui délivrez ici une clé API, vous lui indiquez quel modèle WhatsApp approuvé il a le droit d’envoyer, et vous le reliez à un webhook pour que les réponses vous reviennent. Ce guide parcourt tout le chemin, puis documente l’API REST que vos développeurs appelleront.

Le tableau de bord

Où se trouvent vos clés API

Toutes les clés que vous avez délivrées apparaissent ici, sous « API Keys ». Chaque ligne indique le statut de la clé, son préfixe public (la seule partie de la clé qui vous sera encore affichée après sa création) et le nombre d’appels qu’elle a effectués aujourd’hui. « Manage » ouvre la clé ; l’interrupteur la met en pause sans la supprimer.

Visite guidée en vidéo — 2 étapes, 0:05. Choisissez une étape pour ne lire que ce passage.
Le tableau de bord « API Keys » répertoriant chaque clé délivrée avec son statut, son préfixe public et son nombre d’appels quotidiens

Une clé qui n’est pas encore terminée

Une clé à laquelle aucun modèle n’est associé porte le badge « Needs setup ». Elle peut s’authentifier, mais elle ne peut pas envoyer de message modèle tant que vous n’en avez pas associé un — une intégration à moitié configurée se repère donc d’un coup d’œil, au lieu d’échouer plus tard au moment de l’envoi.

Une ligne de clé API affichant le badge « Needs setup » parce qu’aucun modèle ne lui est encore associé

Configuration guidée

Étape 1 — Que connectez-vous ?

« Connect your software » ouvre un assistant en quatre étapes. Commencez par donner à la clé le nom du système qui l’utilisera, puis choisissez le préréglage correspondant. Les préréglages se contentent de suggérer des noms de champs pertinents — la facturation propose invoice_number, amount, due_date, customer_name — ils ne limitent en rien ce que vous pouvez associer.

Visite guidée en vidéo — 4 étapes, 0:19. Choisissez une étape pour ne lire que ce passage.
Étape 1 de l’assistant : nommer la clé API et choisir le préréglage correspondant au système à connecter

Étape 2 — Choisir le message

Choisissez l’un de vos modèles WhatsApp approuvés. Les modèles sont hébergés chez Meta, pas ici — cette liste est récupérée en direct depuis votre compte WhatsApp connecté, et seuls les modèles APPROUVÉS peuvent être envoyés. Cette étape est facultative : ignorez-la si la clé n’envoie jamais que du texte brut, et associez un modèle plus tard.

Étape 2 de l’assistant : choisir l’un des modèles WhatsApp approuvés récupérés en direct depuis Meta

Étape 3 — Où envoyer les réponses ?

Lorsqu’une personne répond à votre message, ou qu’un rapport de livraison nous revient, nous l’envoyons en POST à une URL que vous contrôlez. Laissez cette option désactivée si votre système ne fait jamais qu’envoyer. Vous pourrez l’ajouter plus tard depuis l’onglet « Webhooks » de la clé — rien n’est définitif ici.

Étape 3 de l’assistant : définir l’URL du webhook vers laquelle sont envoyés les réponses et les rapports de livraison

Étape 4 — Passer en production

C’est en appuyant sur « Next » à l’étape précédente que la clé est réellement créée — cet écran n’en est que la confirmation. Copiez dès maintenant les trois valeurs : la clé API, le secret d’API et le secret de signature du webhook ne sont affichés qu’une seule fois et ne peuvent plus être récupérés ensuite. Si vous perdez le secret, vous devrez le renouveler, ce qui implique de mettre à jour le système qui l’utilisait.

Étape 4 de l’assistant, affichant la clé API, le secret d’API et le secret de signature du webhook une seule et unique fois

Gérer une clé

Vue d’ensemble

L’ouverture d’une clé donne sur l’onglet « Overview » : ce qu’elle est autorisée à faire, l’intensité de son utilisation et les points de terminaison qu’elle peut appeler. Les compteurs d’utilisation sont lus dans le journal des requêtes : ils reflètent donc le trafic réel.

Visite guidée en vidéo — 1 étape, 0:05.
L’onglet « Overview » d’une clé API, montrant ses autorisations, son utilisation et les points de terminaison appelables

Une clé qui reste à configurer

Une clé sans modèle associé s’ouvre à la place sur un onglet « Setup » — le même assistant, intégré. Notez que les onglets diffèrent de la capture précédente : tant qu’une clé n’a pas au moins un modèle, les onglets « Templates » et « Webhooks » ne sont pas proposés, car ils n’auraient encore rien sur quoi agir.

Visite guidée en vidéo — 1 étape, 0:05.
Une clé non configurée s’ouvrant sur l’onglet « Setup », les onglets « Templates » et « Webhooks » n’étant pas encore proposés

Modèles

Dans l’onglet « Templates », chaque carte correspond à un modèle que cette clé peut envoyer. Le corps du texte est le message tel qu’il a été approuvé chez Meta, et les étiquettes en dessous sont les noms des variables que votre système fournira. « Try it » envoie un test, « Get code » génère une requête prête à l’emploi dans huit langages.

Visite guidée en vidéo — 2 étapes, 0:07. Choisissez une étape pour ne lire que ce passage.
L’onglet « Templates », une carte par modèle avec son corps de texte approuvé et les étiquettes des noms de variables

Lire les variables — le point qui mérite qu’on s’y attarde

Regardez la carte payment_failed_alert. Son message enchaîne {{1}}, puis {{3}}, puis {{2}} — dans le texte, les espaces réservés ne sont PAS dans l’ordre numérique. Les étiquettes en dessous, elles, sont listées dans l’ordre numérique : customer_name remplit {{1}}, renewal_date remplit {{2}}, plan_name remplit {{3}}. Faites correspondre vos valeurs au NUMÉRO, jamais à l’ordre dans lequel les espaces réservés apparaissent dans la phrase. C’est le piège le plus fréquent avec les modèles traduits, où l’ordre des mots déplace les espaces réservés.

Une carte de modèle dont les espaces réservés apparaissent dans le désordre numérique dans le corps du message

Les trois boutons de chaque modèle

« Try it » — envoyer un test sans écrire une ligne de code

Le bouton en forme d’avion en papier ouvre ce modèle prérempli avec des valeurs d’exemple, une par nom de variable. C’est le moyen le plus rapide de vérifier qu’une association est correcte avant que vos développeurs n’y touchent. Notez l’interrupteur : « Simulate Only » est la valeur par défaut et rien ne sort de la maison — le serveur vous renvoie votre charge utile telle quelle. Basculez-le sur « Send Real Message » et un vrai message WhatsApp part vers le numéro indiqué, et il est facturé.

Visite guidée en vidéo — 7 étapes, 0:21. Choisissez une étape pour ne lire que ce passage.
La boîte de dialogue « Try it », préremplie d’une valeur d’exemple par variable, avec l’interrupteur « Simulate Only »

Ce que vous renvoie un envoi simulé

Appuyer sur « Send Request » en mode « Simulate Only » renvoie la requête telle que la plateforme l’a reçue, avec le statut HTTP et le temps d’aller-retour. Voyez-y une vérification de forme, pas une répétition générale : cela confirme que votre JSON a été analysé et que la clé a été acceptée, mais cela ne valide PAS vos noms de variables face à l’association, et Meta n’est jamais contacté. Une charge utile qui se simule proprement peut tout de même être rejetée pour de vrai. Pour prouver l’association elle-même, activez « Send Real Message » et envoyez sur votre propre téléphone.

La réponse d’un envoi simulé, qui renvoie la requête avec son statut HTTP et son temps d’aller-retour

« Get code » — la requête, écrite pour vous

Le bouton en chevrons génère une requête fonctionnelle pour ce modèle précis dans huit langages : cURL, JavaScript, Node.js, Python, PHP, Ruby, Go et C#. Transmettez-la à la personne qui écrit l’intégration. Les variables apparaissent sous forme de {{placeholders}} nommés d’après votre association, et les identifiants restent sous la forme YOUR_API_KEY / YOUR_API_SECRET — cette boîte de dialogue n’affiche jamais votre vraie clé, l’extrait peut donc être collé sans risque dans un ticket.

La boîte de dialogue « Get code » montrant une requête prête à l’emploi pour ce modèle, avec des identifiants fictifs

Changer de langage

Cliquez sur n’importe quel langage en haut et l’extrait est réécrit pour lui — le même appel, les mêmes en-têtes, le client HTTP de ce langage. Chacun est généré au premier clic : un bref indicateur de chargement lors du premier changement est donc normal. « Copy » reprend l’extrait tel qu’il s’affiche : il est complet, imports compris, et si cette clé exige des signatures, l’étape de signature y figure déjà.

La même requête réécrite pour un autre langage après un clic dans la rangée des langages

« Edit » — modifier ce que votre système envoie

Le crayon ouvre l’association elle-même. La maquette de téléphone en haut est le message réel avec vos noms actuels substitués : vous voyez donc ce que le client lira. En dessous, chaque variable détectée peut être renommée, réordonnée, marquée comme obligatoire ou typée — une date, un montant, une image. Renommer une variable la renomme aussi dans votre charge utile d’API : un système qui envoie déjà l’ancien nom se mettra donc à échouer. Changez les deux ensemble.

La boîte de dialogue d’édition de l’association, avec un aperçu téléphone au-dessus de la liste de variables renommables et réordonnables

Test API — le même outil, n’importe quel point de terminaison

« Test API », en haut de l’onglet, c’est « Try it » sans le verrou du modèle : choisissez l’un des trois points de terminaison d’envoi, changez de modèle depuis la seconde liste déroulante et modifiez le corps librement. Utilisez-le pour vérifier les appels bruts /messages/text ou /messages/template, qui n’ont pas de carte propre parce qu’ils ne sont liés à aucune association. Notez que les variables de ce modèle s’appellent encore body_1, body_2, body_3 — voilà à quoi ressemble un modèle non associé, et c’est précisément ce que « Edit » sert à corriger.

La boîte de dialogue « Test API » avec une liste de points de terminaison, une liste de modèles et un corps librement modifiable

Réponses

Webhooks — recevoir les réponses

Envoyer ne représente que la moitié d’une intégration. Dirigez un webhook vers votre propre point de terminaison HTTPS et WizMessage y enverra en POST les messages entrants et les mises à jour de livraison. Le secret de signature n’est affiché qu’une fois, au moment de sa génération ; vérifiez-le à chaque livraison pour être certain que l’appel vient bien de nous.

L’onglet « Webhooks », où un point de terminaison HTTPS reçoit les messages entrants et les mises à jour de livraison

Quels événements vous recevez

Cochez les événements que cet endpoint doit recevoir. Chacun arrive dans son propre POST.

ÉvénementQuand il se déclenche
message.receivedUn client vous a envoyé un message.
message.reactionQuelqu'un a ajouté ou retiré une réaction emoji. Se déclenche dans les deux sens — voir Réactions.
message.sentUn message est parti — depuis l'API, depuis le tableau de bord web, ou tapé sur le téléphone de l'entreprise. data.source vous indique lequel.
message.deliveredLe message a atteint l'appareil du destinataire.
message.readLe destinataire l'a ouvert.
message.failedL'envoi ou la livraison a échoué ; data.error en donne la raison.

Note : message.received et les message.reaction entrantes ne sont livrés que si le système de chatbot du compte est réglé sur Webhook. Réglé sur autre chose, les messages entrants partent vers le bot et votre endpoint n'est jamais appelé — l'onglet Webhooks vous en avertit le cas échéant. Les réactions envoyées par l'entreprise elle-même (direction: "outbound") ne sont pas concernées par ce réglage.

Note : Les réactions que l'entreprise envoie depuis son propre téléphone arrivaient auparavant en message.sent avec le corps "[Unsupported message type]". Elles arrivent désormais en message.reaction avec direction: "outbound". Cochez le nouvel événement si vous vous appuyiez dessus.

Les livraisons sont au moins une fois (at-least-once). Dédupliquez sur event combiné à data.message_id plutôt que sur le seul en-tête X-Webhook-Id, et répondez 2xx rapidement.

Référence de l’API REST

Tout ce qui précède sert à configurer la clé. Voici ce que vos développeurs appellent réellement. L’URL de base est https://wizmessage.com/api/v1/external. Chaque point de terminaison exige les en-têtes d’authentification ci-dessous et reste soumis aux limites de débit de la clé.

S’authentifier

Envoyez votre clé API à chaque requête. Si l’option « Require signature » est activée sur la clé, vous devez également signer la requête — et une clé sur laquelle elle est activée rejettera purement et simplement tout appel non signé.

En-têteValeur
X-API-KeyVotre clé API — la valeur pk_live_… complète, et non le seul préfixe affiché dans le tableau de bord.
X-TimestampHorodatage Unix en secondes. Rejeté s’il s’écarte de plus de 5 minutes de l’heure du serveur.
X-API-SignatureHMAC-SHA256 de timestamp + "." + rawBody, avec pour clé SHA256(api_secret), en hexadécimal minuscule.

Remarque : Les signatures ne sont exigées que lorsque require_signature est activé sur la clé. Si ce n’est pas le cas, la clé API suffit à elle seule pour envoyer des messages — c’est précisément pour cela que la clé est un secret, et non un identifiant. Traitez-la comme un mot de passe : elle n’est affichée qu’une fois, et quiconque la détient peut écrire à vos clients.

const crypto = require('node:crypto')

const body = JSON.stringify({ to: '+919876543210', message: 'Hello' })
const timestamp = Math.floor(Date.now() / 1000).toString()

const secretHash = crypto.createHash('sha256').update(API_SECRET).digest('hex')
const signature = crypto.createHmac('sha256', secretHash)
	.update(timestamp + '.' + body)
	.digest('hex')

await fetch('https://wizmessage.com/api/v1/external/messages/text', {
	method: 'POST',
	headers: {
		'Content-Type': 'application/json',
		'X-API-Key': API_KEY,
		'X-Timestamp': timestamp,
		'X-API-Signature': signature,
	},
	body,
})

Points de terminaison

MéthodeCheminObjectif
POST/messages/textEnvoyer un message en texte brut. Ne fonctionne qu’à l’intérieur d’une fenêtre de conversation ouverte de 24 heures.
POST/messages/mediaEnvoyer un document, une image, une vidéo ou un fichier audio. Ne fonctionne que dans une fenêtre de conversation ouverte de 24 heures.
POST/messages/reactionRéagir à un message avec un emoji, ou retirer une réaction que vous avez envoyée.
POST/messages/templateEnvoyer un modèle en fournissant vous-même le tableau de composants brut de Meta.
POST/messages/template/sendEnvoyer un modèle en utilisant l’association que vous avez configurée dans le tableau de bord. C’est celui à utiliser.
POST/messages/interactive/listEnvoyer un message de type liste interactive.
POST/messages/interactive/buttonEnvoyer jusqu’à trois boutons de réponse.
GET/messages/:uuidConsulter le statut de livraison d’un message que vous avez envoyé.
POST/media/uploadTéléverser un PDF, une image ou une vidéo vers Meta et obtenir un media_id à utiliser dans l’en-tête d’un modèle.
GET/templatesLister les modèles accessibles à cette clé.
GET/templates/:nameRécupérer les détails d’un modèle.

Envoyer un modèle associé

Voici la charge utile qui va de pair avec l’onglet « Templates ». Vous envoyez les variables par NOM — les noms affichés sous forme d’étiquettes sur la carte du modèle — et la plateforme assemble les composants de Meta à votre place. C’est tout l’intérêt d’associer un modèle : votre logiciel de facturation n’a besoin de rien connaître du format de composants de Meta.

POST /api/v1/external/messages/template/send

{
	"to": "+919876543210",
	"template_name": "payment_failed_alert",
	"language": "en",
	"variables": {
		"customer_name": "Priya Sharma",
		"renewal_date": "12 Aug 2026",
		"plan_name": "Wiz Pro"
	},
	"reference": "invoice-2026-0817"
}

Remarque : Les noms des variables doivent correspondre exactement à l’association — un nom non reconnu est ignoré, et l’absence d’un nom obligatoire entraîne un rejet avec un 400. reference vous appartient : il vous est renvoyé sur les webhooks, ce qui vous permet de relier un rapport de livraison à un enregistrement de votre propre système.

Limites de débit

Chaque clé possède ses propres limites, visibles dans l’onglet « Overview » — 60 requêtes/minute et 1 000 messages/jour par défaut. Les réponses indiquent le quota restant : levez le pied quand il s’épuise plutôt que de réessayer en butant contre le mur.

Envoyer des fichiers avec l'API

Dans une fenêtre ouverte de 24 heures, votre logiciel peut envoyer un fichier directement — sans gabarit ni validation. Un seul endpoint couvre documents, images, vidéo et audio.

Les deux façons de joindre un fichier à un message médiaPOST /messages/medialinkune URL publique transmise à WhatsApprécupérée à chaque envoiidéal si vous hébergez déjà le fichiermedia_idtéléversez une fois, réutilisez l’idvalable 30 joursidéal pour un fichier souvent envoyéLe lien doit être accessible publiquement : c'est WhatsApp qui le récupère, votre serveur ne l'envoie pas.
Chaque envoi média porte exactement l'un des deux. Si les deux sont fournis, media_id l'emporte.

Ce que vous pouvez envoyer

Le type indiqué dans la requête fixe les limites. Tout fichier plus lourd, ou dans un format non listé, est refusé avant d'atteindre WhatsApp.

TypeFormatsTaille maxLégendeNom de fichier
documentpdf, doc, docx, xls, xlsx, ppt, pptx, txt100 MB
imagejpg, jpeg, png, webp5 MB
videomp4, 3gp16 MB
audioaac, amr, mp3, ogg, m4a16 MB
POST /api/v1/external/messages/media

{
	"to": "+919876543210",
	"type": "document",
	"link": "https://example.com/invoice-2026-0817.pdf",
	"filename": "Invoice 2026-0817.pdf",
	"caption": "Your invoice for August",
	"reference": "invoice-2026-0817"
}

Téléverser une fois, envoyer souvent

Si vous envoyez souvent le même fichier — tarif, catalogue, PDF de conditions — téléversez-le une fois et conservez l'id. Il reste valable 30 jours et évite à WhatsApp de retélécharger votre URL à chaque message.

Téléverser un fichier une fois et l’envoyer plusieurs foisPOST /media/uploadle fichier lui-mêmemedia_idvalable 30 joursPOST /messages/mediaenvoyez-le autant que vouluLivréles webhooks rapportent le résultat
L'id est réutilisable jusqu'à expiration ; seule l'étape d'envoi se répète.
POST /api/v1/external/messages/media

{
	"to": "+919876543210",
	"type": "image",
	"media_id": "1234567890",
	"caption": "This month’s catalogue"
}

Quand un envoi est refusé

  • Hors de la fenêtre de 24 heures. Le média libre exige une conversation ouverte. Envoyez plutôt un gabarit approuvé — voir le guide de la boîte partagée.
  • La clé n'a pas send_media. Vérifiez les permissions dans l'onglet Overview de la clé.
  • Une légende sur de l'audio. WhatsApp n'en affiche pas, elle est donc refusée plutôt qu'ignorée en silence.
  • Un nom de fichier sur autre chose qu'un document. Seuls les documents affichent un nom.
  • Ni media_id ni link. Exactement l'un des deux est requis.

Note : Vous pouvez tout essayer sans écrire de code. Ouvrez la clé, appuyez sur Test API et choisissez POST /messages/media dans la liste des endpoints — le corps est prérempli et « Get code » écrit la même requête en huit langages.

Réactions

Une réaction, c'est l'emoji que quelqu'un pose sur un message précis — 👍 sur une confirmation de commande, ❤️ sur une photo. Ce n'est pas un message à part entière : il en désigne un. Votre logiciel peut lire les réactions au fil de l'eau, et en envoyer.

Un événement, trois origines

Une réaction peut venir de votre client, du propriétaire qui la pose sur le téléphone de l'app Business, ou de votre propre logiciel via l'API. Les trois arrivent sur le même événement message.reaction : un seul gestionnaire couvre tous les cas — direction et source vous disent lequel vous avez sous les yeux.

Les trois origines d'une réaction et les champs qui identifient chacuneVotre clientil a réagi à vousdirection: inboundpas de champ sourcecontact indique quiLe téléphone Businessle propriétaire a tapédirection: outboundsource: mobileVotre logicielvous avez appelé l APIdirection: outboundsource: apireference est renvoyéSeules les réactions entrantes portent des coordonnées — une sortante, c'est l'entreprise, que vous connaissez déjà.
Un événement, trois origines. Lisez direction d'abord, source ensuite.

À quoi ressemble un webhook de réaction

Ici, un client ajoute 👍 à un message que vous lui avez envoyé.

{
	"version": 1,
	"event": "message.reaction",
	"timestamp": "2026-08-01T09:14:22.031Z",
	"data": {
		"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgSQjkz…",
		"direction": "inbound",
		"from": "+919876543210",
		"to": "+911122334455",
		"reaction": {
			"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…",
			"emoji": "👍",
			"action": "added"
		},
		"contact": {
			"name": "Priya Sharma",
			"wa_id": "919876543210"
		}
	}
}

Il y a deux message_id dans ce payload, et ils ne désignent pas la même chose. C'est le champ qui mérite une deuxième lecture.

ChampCe que c'est
data.message_idL'id de la réaction elle-même — pas celui du message auquel on a réagi.
data.reaction.message_idLe wamid du message auquel on a réagi. C'est celui que vous rapprochez de vos propres enregistrements.
data.reaction.emojiL'emoji. Chaîne vide lorsque la réaction a été retirée.
data.reaction.actionadded ou removed. Déduit de l'emoji, vous pouvez donc brancher directement dessus.
data.directioninbound — le client a réagi. outbound — c'est l'entreprise.
data.sourceSortant uniquement : mobile (téléphone de l'app Business) ou api (votre logiciel). Absent en entrant.
data.from / data.toRelatifs au sens, comme pour message.sent : entrant c'est client → entreprise, sortant c'est entreprise → client.
data.referenceUniquement sur les réactions envoyées par votre propre logiciel, et seulement si vous en avez fourni une.
data.contact, data.user_id, data.usernameEntrant uniquement — qui a réagi. Absents sur toute réaction sortante.

Note : data.from est normalement le numéro de téléphone du client, mais WhatsApp n'en envoie pas toujours un. Dans ce cas vous recevez son identifiant utilisateur WhatsApp — traitez donc from comme un identifiant, pas comme un numéro que vous pourrez toujours appeler.

Ajouter et retirer une réaction

Retirer une réaction est un événement à part, avec un emoji vide et action: "removed".

{
	"event": "message.reaction",
	"data": {
		"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgSN0U3…",
		"direction": "inbound",
		"reaction": {
			"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…",
			"emoji": "",
			"action": "removed"
		}
	}
}

Chaque appui est un message WhatsApp distinct, avec son propre id. Réagir 👍, changer pour ❤️, puis retirer complètement donne trois événements, dont aucun ne remplace les autres dans votre stockage. Indexez sur data.reaction.message_id et gardez le plus récent — c'est la réaction actuelle du message.

Envoyer une réaction depuis votre logiciel

Le même endpoint ajoute et retire. Ce qu'il fait dépend uniquement de la présence d'un emoji.

Comment le même endpoint de réaction ajoute et retire une réactionPOST /messages/reactionun emojil emoji que vous voulez montrerun par personne et par messageajoute la réactionun emoji videefface ce que vous aviez envoyéenvoyez le champ, mais videla retirePour retirer une réaction, envoyez emoji comme chaîne vide. Omettre le champ est rejeté.
Un endpoint, deux résultats. Le champ emoji décide.
POST /api/v1/external/messages/reaction

{
	"to": "+919876543210",
	"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…",
	"emoji": "👍",
	"reference": "invoice-2026-0817"
}

Ici, message_id est l'id WhatsApp — le whatsapp_message_id d'une réponse d'envoi, ou le data.reaction.message_id d'un webhook. Ce n'est jamais l'un de nos uuid. Les réactions utilisent la permission send_text : toute clé qui peut écrire au client peut réagir.

Note : Une réaction ne produit jamais message.delivered ni message.read. WhatsApp ne les émet pas pour une réaction — le seul événement de suivi possible est message.failed, lorsque la réaction est rejetée. La consulter avec GET /messages/:uuid fonctionne comme pour tout autre envoi.

Quand une réaction est refusée

Certains refus se produisent ici, avant que quoi que ce soit n'atteigne WhatsApp. Ils renvoient une erreur à votre appel API et ne produisent aucun webhook — il n'y a rien à signaler, puisque rien n'a été envoyé.

  • La clé n'a pas send_text. 403. Les réactions réutilisent cette permission ; il n'y en a pas d'autre à accorder.
  • to n'est pas au format E.164. 422. Le même format +… que pour tout autre endpoint.
  • message_id est absent ou vide. 422. Jusqu'à 255 caractères.
  • emoji est absent. 422. Pour retirer une réaction, envoyez "emoji": "" — envoyez le champ vide, ne l'omettez pas. Jusqu'à 16 caractères.

Les autres se produisent chez WhatsApp, après que nous avons accepté l'appel. Vous recevez d'abord un 200 et un webhook message.reaction, puis un message.failed portant l'erreur 131009 une fois que WhatsApp l'a rejetée.

  • Le message a plus de 30 jours. C'est la limite de WhatsApp pour réagir, pas la nôtre — nous ne vérifions pas l'ancienneté.
  • Le message n'appartient pas à cette conversation. Un wamid d'une autre discussion, ou l'un de nos uuid passé par erreur.
  • Le message a été supprimé, ou est lui-même une réaction. On ne peut pas réagir à une réaction.

Note : Si l'envoi échoue d'emblée au lieu d'être rejeté plus tard, vous recevez un 500 et un message.failed dont le data.message_id est notre uuid, pas un wamid — il n'y a aucun id WhatsApp à signaler, puisque WhatsApp ne l'a jamais accepté.

Note : Comme pour tout autre endpoint, vous pouvez essayer sans écrire de code. Ouvrez la clé, appuyez sur Test API et choisissez POST /messages/reaction — réagissez à un message sur votre propre téléphone et regardez le webhook arriver.