Guides du produit
Webhooks de messages entrants (écouteurs)
Un écouteur (listener) relie l'un de vos numéros WhatsApp à Unibox et peut envoyer un webhook de message entrant à votre serveur. Dans le tableau de bord, les écouteurs se gèrent sur la page Déclencheurs entrants. Une fois qu'un numéro est un écouteur, ses discussions sont synchronisées dans la boîte de réception Unibox et, si vous ajoutez une URL de webhook, Wbiztool envoie chaque nouveau message à votre serveur. Utilisez les webhooks pour enregistrer les conversations dans un CRM, alerter votre équipe ou créer une réponse automatique.
Avant de commencer#
- L'option Unibox. Elle coûte 20 $/mois ou 200 $/an par numéro WhatsApp, et le nombre d'écouteurs dont vous pouvez disposer est égal à la quantité de l'option. Achetez-la sous Modules Complémentaires Disponibles dans Facturation et forfaits, dans le tableau de bord. Sans elle, la page Déclencheurs entrants s'ouvre quand même, mais cliquer sur Ajouter un Nouvel Écouteur affiche Unibox Add-on Required (option Unibox requise) avec un bouton Subscribe to Unibox Add-on (s'abonner à l'option Unibox).
- Un numéro WhatsApp connecté dans les paramètres WhatsApp. Seuls les numéros connectés qui ne sont pas encore des écouteurs peuvent être ajoutés.
- Vous devez être propriétaire (owner) ou éditeur de l'espace de travail.
- Pour les webhooks : une URL publique (utilisez
https) de 100 caractères maximum qui accepte les requêtesPOSTavec un corps JSON.
Ajouter un écouteur#
Ouvrez Déclencheurs entrants
Dans la barre latérale, ouvrez Unibox et cliquez sur Déclencheurs entrants, ou allez sur Déclencheurs entrants.
Créez un nouvel écouteur
Cliquez sur la carte Ajouter un Nouvel Écouteur.
Choisissez le numéro
Choisissez-le dans Sélectionner le numéro WhatsApp. Si la liste indique No available WhatsApp numbers (aucun numéro WhatsApp disponible), tous les numéros connectés sont déjà des écouteurs, ou aucun numéro n'est connecté.
Ajoutez un webhook (facultatif)
Saisissez votre URL Webhook (Optionnel). Dès que vous saisissez une URL, Webhook Events (événements du webhook) apparaît : laissez cochés Incoming Messages (messages entrants), Outgoing Messages (messages sortants) ou les deux. Vous pourrez ajouter ou modifier l'URL plus tard.
Enregistrez
Cliquez sur Ajouter un Écouteur. L'écouteur apparaît sous forme de carte avec le statut Active (actif). Si vous avez saisi une URL, un secret de webhook est créé pour celle-ci.
Gérer les écouteurs#
Chaque carte affiche le numéro, son statut, l'URL Webhook : (ou Non configuré), la Dernière Activité : (la dernière vérification des messages du numéro, ou Jamais) et le Secret Webhook :, masqué jusqu'à ce que vous cliquiez sur le bouton en forme d'œil.
Ouvrez le menu ⋮ d'une carte pour :
| Action | Effet |
|---|---|
| Modifier | Modifier l'URL du webhook, les événements du webhook ou le secret. Le numéro ne peut pas être modifié. |
| Désactiver / Activer | La désactivation passe l'écouteur au statut Inactive (inactif) : le numéro n'est plus synchronisé dans la boîte de réception et aucun webhook n'est envoyé. L'activation le repasse en Active. |
| Supprimer | Supprime l'écouteur après confirmation. Les conversations déjà présentes dans la boîte de réception sont conservées. Ajouter à nouveau le même numéro plus tard restaure l'écouteur. Si vous saisissez une URL de webhook lors de ce nouvel ajout, l'écouteur conserve son ancien secret de webhook, s'il en avait un, au lieu d'en recevoir un nouveau. |
Statuts des écouteurs#
| Statut | Signification |
|---|---|
| Active (actif) | Les messages sont synchronisés et les webhooks envoyés tant que le numéro est connecté. |
| Pending (en attente) | Le numéro n'était pas connecté lors de la création de l'écouteur, par exemple par Zapier. Cliquez sur Activer une fois le numéro connecté. |
| Inactive (inactif) | Désactivé. Rien n'est synchronisé et aucun webhook n'est envoyé. |
Statistiques#
| Carte | Ce qu'elle affiche |
|---|---|
| Écouteurs Actifs | Tous les écouteurs de la page, y compris ceux qui sont désactivés. |
| Numéros Disponibles | Les numéros connectés qui ne sont pas encore des écouteurs. Les numéros dont vous avez supprimé l'écouteur sont toujours comptés comme utilisés ici : cette carte peut donc afficher moins de numéros que vous ne pouvez réellement en ajouter. |
| Limite Totale | Le nombre d'écouteurs autorisés par votre option Unibox. |
| Messages Aujourd'hui | Pas encore comptabilisé ; affiche toujours 0. |
Modifier l'URL ou les événements du webhook#
Ouvrez l'écouteur
Cliquez sur ⋮ sur la carte, puis sur Modifier.
Mettez à jour les paramètres
Modifiez Webhook URL (Optional) (URL du webhook, facultative) et Webhook Events. Effacez l'URL pour arrêter les webhooks de ce numéro : son secret est alors supprimé aussi, et un nouveau secret est créé si vous ajoutez à nouveau une URL.
Enregistrez
Cliquez sur Update Listener (mettre à jour l'écouteur).
Régénérer le secret#
Dans Edit Listener (modifier l'écouteur), cliquez sur le bouton d'actualisation à côté de Webhook Secret (secret du webhook) et confirmez. Le nouveau secret est enregistré immédiatement, même si vous fermez ensuite la fenêtre sans cliquer sur Update Listener, et les requêtes sont signées avec ce secret à partir de ce moment. Mettez immédiatement à jour votre serveur avec le nouveau secret.
Comment les webhooks sont envoyés#
Wbiztool envoie une requête POST à votre URL pour chaque nouveau message trouvé lors de la synchronisation du numéro, qui a lieu toutes les quelques minutes tant que le numéro est connecté et n'est pas occupé à envoyer des messages.
- Événements :
message_receivedpour les messages que l'on envoie à votre numéro, etmessage_sentpour les messages envoyés depuis celui-ci (depuis le téléphone, des campagnes ou l'API). Seuls les événements cochés dans Webhook Events sont envoyés. - Les réponses envoyées depuis la boîte de réception Unibox ne déclenchent généralement pas
message_sent, car la boîte de réception les contient déjà au moment de la synchronisation. - Réponse : répondez avec un HTTP
200en moins de 8 secondes. Toute autre réponse, ou un dépassement de délai, compte comme un échec de livraison. - Aucune nouvelle tentative : chaque message n'est envoyé qu'une fois. Si votre serveur est indisponible, ce webhook est perdu.
- Ordre : les requêtes sont envoyées indépendamment et peuvent arriver dans le désordre. Triez selon
message.timestampsi l'ordre compte.
En-têtes#
| En-tête | Valeur |
|---|---|
Content-Type | application/json |
X-Wbiztool-Event | message_received ou message_sent |
X-Wbiztool-Timestamp | L'heure d'envoi du webhook, au format ISO 8601 UTC. Identique à timestamp dans le corps. |
X-Wbiztool-Webhook-Id | L'identifiant de l'écouteur. Identique à webhook_id dans le corps. |
X-Wbiztool-Signature | sha256= suivi de la signature. Envoyé dès que l'écouteur possède un secret, ce qui est toujours le cas lorsqu'une URL est définie. |
Payload#
{
"event": "message_received",
"timestamp": "2026-09-16T10:31:12.482913+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected]_3EB0C1A2B3D4E5F60718",
"type": "chat",
"content": "Hi, is my order #4821 out for delivery?",
"from": "919876543210",
"from_name": "Aman",
"to": "919812345678",
"timestamp": "2026-09-16T10:29:58+00:00",
"whatsapp_timestamp": 1789554598,
"direction": "incoming",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "919876543210",
"name": "Aman",
"is_group": false,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
}
}{
"event": "message_sent",
"timestamp": "2026-09-16T10:33:40.117205+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected]_3EB0F9E8D7C6B5A40312",
"type": "chat",
"content": "Yes, it will reach you today by 6 PM.",
"from": "919876543210",
"from_name": "Aman",
"to": "919812345678",
"timestamp": "2026-09-16T10:31:04+00:00",
"whatsapp_timestamp": 1789554664,
"direction": "outgoing",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "919876543210",
"name": "Aman",
"is_group": false,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
}
}{
"event": "message_received",
"timestamp": "2026-09-16T10:36:02.904311+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected]_3EB0A7B6C5D4E3F20109",
"type": "image",
"content": "📸 Image",
"from": "919876543210",
"from_name": "Aman",
"to": "919812345678",
"timestamp": "2026-09-16T10:34:51+00:00",
"whatsapp_timestamp": 1789554891,
"direction": "incoming",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0,
"media": {
"filename": "",
"mimetype": "image/jpeg",
"size": 245760
}
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "919876543210",
"name": "Aman",
"is_group": false,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
}
}{
"event": "message_received",
"timestamp": "2026-09-16T10:40:15.330187+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected][email protected]",
"type": "chat",
"content": "Is the store open on Sunday?",
"from": "120363041234567890",
"from_name": "Acme Loyalty Club",
"to": "919812345678",
"timestamp": "2026-09-16T10:39:02+00:00",
"whatsapp_timestamp": 1789555142,
"direction": "incoming",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "120363041234567890",
"name": "Acme Loyalty Club",
"is_group": true,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
},
"group": {
"name": "Acme Loyalty Club"
}
}Les numéros, identifiants et noms ci-dessus sont des exemples.
Champs de premier niveau#
| Champ | Type | Description |
|---|---|---|
event | string | message_received ou message_sent. |
timestamp | string | L'heure d'envoi du webhook (ISO 8601, UTC). |
webhook_id | integer | L'identifiant de l'écouteur. |
whatsapp_client_id | string | L'identifiant de votre numéro WhatsApp, tel qu'affiché dans les paramètres WhatsApp. |
whatsapp_phone | string | Votre numéro WhatsApp. |
message | object | Le message. Voir ci-dessous. |
contact | object | La personne ou le groupe avec qui la conversation a lieu. Voir ci-dessous. |
organisation | object | id (string) et name de votre espace de travail. |
group | object | Uniquement pour les discussions de groupe : name, le nom du groupe. |
Champs de message#
| Champ | Type | Description |
|---|---|---|
id | string | L'identifiant WhatsApp du message. Utilisez-le pour ignorer les doublons. |
type | string | chat pour le texte. Sinon, le type WhatsApp, par exemple image, video, audio, ptt (message vocal), document, sticker ou location. |
content | string | Le texte pour les messages chat. Pour les médias, un libellé à la place : 📸 Image, 🎥 Video, 🎵 Audio, 🎤 Voice Message, 😊 Sticker, Document: suivi du texte du document (Document lorsqu'il n'y a pas de texte), ou le nom du type avec une majuscule initiale pour tout le reste, par exemple Location. Les légendes ne sont pas incluses. |
from | string | Toujours le numéro du contact (ou l'identifiant du groupe), dans les deux sens. |
from_name | string | Le nom du contact ou du groupe. Peut être vide. |
to | string | Toujours votre numéro WhatsApp, dans les deux sens. |
timestamp | string | L'heure d'envoi du message sur WhatsApp (ISO 8601, UTC). |
whatsapp_timestamp | integer | La même heure sous forme de timestamp Unix en secondes. |
direction | string | incoming ou outgoing. Utilisez ce champ, et non from et to, pour connaître le sens. |
status | string | Actuellement toujours pending. Ne vous y fiez pas pour le statut de livraison ou de lecture. |
is_forwarded | boolean | Indique si le message a été transféré. |
forwarding_score | integer | Le nombre de fois où il a été transféré. |
media | object | Pour les messages médias, lorsque les informations sont disponibles : filename, mimetype et size en octets. Le fichier lui-même n'est pas inclus. |
quoted_message_id | string | Uniquement lorsque le message répond à un autre message. |
Champs de contact#
| Champ | Type | Description |
|---|---|---|
whatsapp_id | string | L'identifiant WhatsApp, par exemple [email protected] pour une personne ou …@g.us pour un groupe. |
phone | string | Le numéro sans +, ou l'identifiant du groupe pour les groupes. |
name | string | Le nom que Wbiztool connaît pour le contact, ou le nom du groupe. Peut être vide. |
is_group | boolean | true pour les discussions de groupe. |
is_business | boolean | true pour les comptes WhatsApp Business, lorsque l'information est connue. |
Vérifier la signature#
Chaque requête est signée avec le secret de votre écouteur au moyen de HMAC-SHA256. La signature est calculée sur le corps brut de la requête, exactement tel qu'il est reçu, et envoyée dans X-Wbiztool-Signature sous la forme sha256= suivi du condensat hexadécimal en minuscules.
Calculez toujours la signature à partir des octets bruts, avant d'analyser le JSON. Analyser puis réencoder le corps le modifie (par exemple, les caractères non anglais et les emoji arrivent échappés sous la forme \uXXXX), et la signature ne correspondra pas.
// Express: keep the raw body for this route
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.WBIZTOOL_WEBHOOK_SECRET;
app.post("/wbiztool/webhook", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
const received = req.get("X-Wbiztool-Signature") || "";
const valid =
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send("Invalid signature");
const data = JSON.parse(req.body.toString("utf8"));
if (data.event === "message_received") {
console.log(`New message from ${data.contact.phone}: ${data.message.content}`);
}
res.sendStatus(200); // reply quickly; do slow work in the background
});
app.listen(3000);# Flask
import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["WBIZTOOL_WEBHOOK_SECRET"].encode()
@app.post("/wbiztool/webhook")
def wbiztool_webhook():
raw_body = request.get_data() # raw bytes, before parsing
expected = "sha256=" + hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
received = request.headers.get("X-Wbiztool-Signature", "")
if not hmac.compare_digest(expected, received):
abort(401)
data = json.loads(raw_body)
if data["event"] == "message_received":
print(f"New message from {data['contact']['phone']}: {data['message']['content']}")
return "", 200<?php
$secret = getenv('WBIZTOOL_WEBHOOK_SECRET');
$rawBody = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
$received = $_SERVER['HTTP_X_WBIZTOOL_SIGNATURE'] ?? '';
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit('Invalid signature');
}
$data = json_decode($rawBody, true);
if ($data['event'] === 'message_received') {
error_log('New message from ' . $data['contact']['phone'] . ': ' . $data['message']['content']);
}
http_response_code(200);La signature ne couvre pas l'en-tête d'horodatage : elle ne protège donc pas contre le rejeu d'une requête. Si c'est important pour vous, enregistrez chaque message.id déjà traité et ignorez les répétitions.
Dépannage#
| Message ou problème | Que faire |
|---|---|
| Unibox Add-on Required (option Unibox requise) lorsque vous cliquez sur Ajouter un Nouvel Écouteur | Votre espace de travail ne dispose pas de l'option Unibox. Achetez-la sous Modules Complémentaires Disponibles dans Facturation et forfaits, dans le tableau de bord. |
You have reached your unibox numbers limit | Supprimez un écouteur dont vous n'avez plus besoin, ou augmentez la quantité de l'option. |
| No available WhatsApp numbers | Connectez un autre numéro, ou celui-ci est déjà un écouteur. |
This WhatsApp number is already a listener | Modifiez plutôt la carte existante. |
Invalid WhatsApp client | Le numéro s'est déconnecté. Reconnectez-le dans les paramètres WhatsApp et rechargez la page. |
Erreur mentionnant value too long lors de l'enregistrement | L'URL du webhook dépasse 100 caractères. Utilisez une URL plus courte. |
| Aucun webhook n'arrive | Vérifiez que l'écouteur est Active, que le numéro est connecté, que le type d'événement est coché et que votre URL est publique, en https avec un certificat valide. Les messages ne sont envoyés qu'après la synchronisation suivante, quelques minutes plus tard. |
| Certains webhooks manquent | Votre serveur a renvoyé autre chose que 200, a mis plus de 8 secondes à répondre ou était injoignable. Les livraisons en échec ne sont pas renvoyées. |
| La signature ne correspond pas | Utilisez le corps brut, et non du JSON réencodé, ainsi que le secret actuel. Régénérer le secret, ou connecter Zapier, le remplace. |
| Dernière Activité : indique Jamais | Le numéro n'a pas encore été vérifié. Il doit être connecté et ne pas être occupé à envoyer des messages. |
