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.


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.

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.


É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 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 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.

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.


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.


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.


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.

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é.


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.

« 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.

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à.

« 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.

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.

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.

Quels événements vous recevez
Cochez les événements que cet endpoint doit recevoir. Chacun arrive dans son propre POST.
| Événement | Quand il se déclenche |
|---|---|
message.received | Un client vous a envoyé un message. |
message.reaction | Quelqu'un a ajouté ou retiré une réaction emoji. Se déclenche dans les deux sens — voir Réactions. |
message.sent | Un 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.delivered | Le message a atteint l'appareil du destinataire. |
message.read | Le destinataire l'a ouvert. |
message.failed | L'envoi ou la livraison a échoué ; data.error en donne la raison. |
Note :
message.receivedet lesmessage.reactionentrantes 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.sentavec le corps"[Unsupported message type]". Elles arrivent désormais enmessage.reactionavecdirection: "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ête | Valeur |
|---|---|
X-API-Key | Votre clé API — la valeur pk_live_… complète, et non le seul préfixe affiché dans le tableau de bord. |
X-Timestamp | Horodatage Unix en secondes. Rejeté s’il s’écarte de plus de 5 minutes de l’heure du serveur. |
X-API-Signature | HMAC-SHA256 de timestamp + "." + rawBody, avec pour clé SHA256(api_secret), en hexadécimal minuscule. |
Remarque : Les signatures ne sont exigées que lorsque
require_signatureest 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éthode | Chemin | Objectif |
|---|---|---|
POST | /messages/text | Envoyer un message en texte brut. Ne fonctionne qu’à l’intérieur d’une fenêtre de conversation ouverte de 24 heures. |
POST | /messages/media | Envoyer 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/reaction | Réagir à un message avec un emoji, ou retirer une réaction que vous avez envoyée. |
POST | /messages/template | Envoyer un modèle en fournissant vous-même le tableau de composants brut de Meta. |
POST | /messages/template/send | Envoyer un modèle en utilisant l’association que vous avez configurée dans le tableau de bord. C’est celui à utiliser. |
POST | /messages/interactive/list | Envoyer un message de type liste interactive. |
POST | /messages/interactive/button | Envoyer jusqu’à trois boutons de réponse. |
GET | /messages/:uuid | Consulter le statut de livraison d’un message que vous avez envoyé. |
POST | /media/upload | Té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 | /templates | Lister les modèles accessibles à cette clé. |
GET | /templates/:name | Ré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.referencevous 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.
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.
| Type | Formats | Taille max | Légende | Nom de fichier |
|---|---|---|---|---|
document | pdf, doc, docx, xls, xlsx, ppt, pptx, txt | 100 MB | ✓ | ✓ |
image | jpg, jpeg, png, webp | 5 MB | ✓ | — |
video | mp4, 3gp | 16 MB | ✓ | — |
audio | aac, amr, mp3, ogg, m4a | 16 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.
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_idnilink. 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/mediadans 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.
À 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.
| Champ | Ce que c'est |
|---|---|
data.message_id | L'id de la réaction elle-même — pas celui du message auquel on a réagi. |
data.reaction.message_id | Le wamid du message auquel on a réagi. C'est celui que vous rapprochez de vos propres enregistrements. |
data.reaction.emoji | L'emoji. Chaîne vide lorsque la réaction a été retirée. |
data.reaction.action | added ou removed. Déduit de l'emoji, vous pouvez donc brancher directement dessus. |
data.direction | inbound — le client a réagi. outbound — c'est l'entreprise. |
data.source | Sortant uniquement : mobile (téléphone de l'app Business) ou api (votre logiciel). Absent en entrant. |
data.from / data.to | Relatifs au sens, comme pour message.sent : entrant c'est client → entreprise, sortant c'est entreprise → client. |
data.reference | Uniquement sur les réactions envoyées par votre propre logiciel, et seulement si vous en avez fourni une. |
data.contact, data.user_id, data.username | Entrant uniquement — qui a réagi. Absents sur toute réaction sortante. |
Note :
data.fromest 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 doncfromcomme 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.
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.deliverednimessage.read. WhatsApp ne les émet pas pour une réaction — le seul événement de suivi possible estmessage.failed, lorsque la réaction est rejetée. La consulter avecGET /messages/:uuidfonctionne 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. ton'est pas au format E.164.422. Le même format+…que pour tout autre endpoint.message_idest absent ou vide.422. Jusqu'à 255 caractères.emojiest 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
wamidd'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
500et unmessage.faileddont ledata.message_idest notre uuid, pas unwamid— 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.