Retour

Documentation API Wazzap AI - Webhooks

Recevez en temps réel les messages WhatsApp entrants de votre agent sur votre propre serveur grâce aux webhooks Wazzap AI

Dernière mise à jour :

Documentation API Wazzap AI - Webhooks

Table des matières

  1. Introduction
  2. Configuration du webhook
  3. Types d'événements supportés
  4. Format des livraisons
  5. Vérification de la signature (HMAC)
  6. Fiabilité et gestion des échecs
  7. Exemples de serveurs webhook
  8. Bonnes pratiques
  9. Dépannage

Introduction

Les webhooks vous permettent de recevoir en temps réel les messages WhatsApp entrants de votre agent directement sur votre serveur, sans avoir à interroger l'API.

À chaque message reçu par votre agent, Wazzap AI envoie une requête POST signée vers l'URL que vous avez configurée.

Caractéristiques principales

  • Livraison en temps réel des messages entrants
  • Signature HMAC-SHA256 de chaque livraison (header X-Wazzap-Signature)
  • Validation de l'URL par un ping de test à l'enregistrement
  • Désactivation automatique après échecs répétés (protection de votre quota)
  • Un webhook par clé API, lié à l'agent de la clé

Cas d'usage typiques

  • Synchroniser les conversations WhatsApp dans votre CRM
  • Déclencher des workflows (Zapier, Make, n8n…) à la réception d'un message
  • Construire votre propre interface de support par-dessus Wazzap AI
  • Journaliser tous les messages entrants pour analyse

Configuration du webhook

Depuis le dashboard

  1. Rendez-vous dans Paramètres → Clés API
  2. Sur la clé API concernée, ouvrez la section Webhook
  3. Renseignez l'URL HTTPS de votre serveur
  4. Wazzap AI envoie immédiatement un ping de validation (webhook.test) — votre serveur doit répondre avec un statut 2xx pour que l'URL soit enregistrée
  5. Copiez le secret HMAC affiché : il sert à vérifier la signature de chaque livraison

Contraintes sur l'URL

Contrainte Détail
Protocole https:// uniquement (http refusé)
Réseau Les adresses privées/locales sont refusées (localhost, 127.x, 10.x, 192.168.x, 172.16-31.x, 169.254.x)
Validation L'URL doit répondre 2xx au ping de test, sinon elle n'est pas enregistrée
Timeout 10 secondes par livraison

Le ping de validation

À l'enregistrement de l'URL, votre serveur reçoit ce payload :

{
  "event": "webhook.test",
  "apiKeyId": "cm9xxx...",
  "agentId": "cm9yyy...",
  "data": {
    "message": "Ping de validation Wazzap — répondez avec un statut 2xx pour valider ce webhook.",
    "timestamp": "2026-07-06T15:00:00.000Z"
  }
}

Répondez simplement 200 OK (le corps de votre réponse est ignoré).


Types d'événements supportés

Événement Déclencheur Description
webhook.test Enregistrement / test de l'URL depuis le dashboard Ping de validation. Répondez 2xx.
message.received Message WhatsApp entrant reçu par votre agent Un contact a envoyé un message à votre numéro WhatsApp connecté.

⚠️ Ce qui ne déclenche PAS de webhook

  • Vos propres envois via l'API /api/wazzap/send-message
  • Les messages tapés manuellement depuis le téléphone connecté (messages fromMe)
  • Les réponses générées par l'IA de votre agent
  • Les accusés de lecture, réactions et statuts

Le webhook est volontairement limité aux messages entrants : c'est l'événement dont vous avez besoin pour réagir à vos contacts. D'autres types d'événements pourront être ajoutés — le champ event du payload vous permet de router chaque livraison et d'ignorer sans erreur les types que vous ne connaissez pas.


Format des livraisons

Headers de la requête

Chaque livraison est un POST avec ces headers :

POST /votre-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Wazzap-Webhook/1.0
X-Wazzap-Signature: sha256=3f5a8c…

Payload message.received — message texte

{
  "event": "message.received",
  "agentId": "cm9yyy...",
  "organizationId": "cm9zzz...",
  "data": {
    "conversationId": "cmr9aaa...",
    "contactId": "cmr9bbb...",
    "phoneNumber": "33612345678",
    "contactName": "Jean Dupont",
    "message": "Bonjour, je voudrais des informations",
    "messageType": "text",
    "fromMe": false,
    "media": null,
    "transcription": null,
    "description": null,
    "messageId": "3EB0A5B4E7CB234BC78A48",
    "timestamp": "2026-07-06T15:18:55.000Z"
  }
}

Payload message.received — message vocal (audio)

Les vocaux sont transcrits automatiquement (Whisper). La transcription arrive dans message ET dans transcription ; le fichier audio (hébergé sur notre CDN, lisible publiquement) est dans media.url.

{
  "event": "message.received",
  "agentId": "cm9yyy...",
  "organizationId": "cm9zzz...",
  "data": {
    "conversationId": "cmr9aaa...",
    "contactId": "cmr9bbb...",
    "phoneNumber": "33612345678",
    "contactName": "Jean Dupont",
    "message": "Bonjour, est-ce que le produit est encore disponible ?",
    "messageType": "audio",
    "fromMe": false,
    "media": {
      "url": "https://pub-xxxx.r2.dev/wazzap-v2.5/audio/1783351406671-xxx.mp3",
      "type": "audio",
      "mimeType": "audio/ogg; codecs=opus"
    },
    "transcription": "Bonjour, est-ce que le produit est encore disponible ?",
    "description": null,
    "messageId": "false_963835624366@lid_3A4355A4022B54D3A211",
    "timestamp": "2026-07-06T15:23:14.000Z"
  }
}

💳 Coût : chaque transcription réussie consomme 1 crédit de votre organisation. Si votre solde est à zéro, la transcription est sautée (le vocal est quand même livré avec son media.url, transcription vaut null). Vous pouvez désactiver la transcription à tout moment via le switch « Transcrire les vocaux » dans la configuration du webhook (Paramètres → Clés API → Webhook).

⚠️ Si la transcription échoue (audio inaudible, service indisponible…), aucun crédit n'est débité et la livraison a quand même lieu : transcription vaut null, message contient le texte/caption éventuel (souvent "" pour un vocal), et media.url vous permet de récupérer l'audio pour le traiter vous-même.

Payload message.received — image

Les images reçues sont analysées par la vision IA : la description générée arrive dans description. message contient la caption tapée par le contact (souvent vide).

{
  "event": "message.received",
  "agentId": "cm9yyy...",
  "organizationId": "cm9zzz...",
  "data": {
    "conversationId": "cmr9aaa...",
    "contactId": "cmr9bbb...",
    "phoneNumber": "33612345678",
    "contactName": "Jean Dupont",
    "message": "C'est bien ce modèle ?",
    "messageType": "image",
    "fromMe": false,
    "media": {
      "url": "https://pub-xxxx.r2.dev/wazzap-v2.5/images/1783351406671-xxx.jpg",
      "type": "image",
      "mimeType": "image/jpeg"
    },
    "transcription": null,
    "description": "Photo d'une paire de baskets blanches avec des lacets rouges…",
    "messageId": "false_963835624366@lid_3A4355A40BB54D3A211",
    "timestamp": "2026-07-06T15:25:02.000Z"
  }
}

Champs du payload

Champ Type Description
event string Type d'événement (message.received)
agentId string ID de l'agent qui a reçu le message
organizationId string ID de votre organisation
data.conversationId string ID de la conversation dans Wazzap AI
data.contactId string ID du contact dans Wazzap AI
data.phoneNumber string Numéro de l'expéditeur (sans le +)
data.contactName string | null Nom du contact (si connu)
data.message string Texte propre du message : texte ou caption ; transcription pour un vocal quand elle a réussi ; "" pour un média muet
data.messageType string text | image | audio | video | document
data.fromMe boolean Sens du message : false = envoyé par le contact (entrant). Toujours false pour message.received — champ explicite, prêt pour de futurs types d'événements
data.media object | null Média joint : { url, type, mimeType }. null pour un message texte. url pointe vers notre CDN public (ou l'URL source si l'upload CDN a échoué)
data.transcription string | null Audio uniquement : transcription automatique du vocal, null si elle a échoué ou n'a pas été tentée
data.description string | null Image uniquement : description générée par la vision IA, null si non analysée
data.messageId string | null ID WhatsApp du message (utilisez-le comme clé d'idempotence)
data.timestamp string Date d'envoi du message (ISO-8601)

Votre réponse

Répondez avec un statut 2xx (idéalement 200) le plus vite possible — traitez le message de façon asynchrone si votre logique est lente. Toute réponse non-2xx ou un timeout (>10 s) est compté comme un échec de livraison.


Vérification de la signature (HMAC)

Chaque livraison est signée avec le secret HMAC de votre webhook :

X-Wazzap-Signature: sha256=<hex(HMAC-SHA256(secret, corps_brut_de_la_requête))>

⚠️ Vérifiez toujours la signature avant de traiter une livraison — c'est votre garantie que la requête vient bien de Wazzap AI et non d'un tiers. Calculez le HMAC sur le corps brut (les octets reçus, avant tout parsing JSON).


Fiabilité et gestion des échecs

Règle Valeur Détail
Timeout de livraison 10 s Au-delà, la livraison est comptée en échec
Échecs consécutifs tolérés 20 Le compteur (webhookFailCount) s'incrémente à chaque échec
Après 20 échecs consécutifs Webhook désactivé Réactivez-le depuis le dashboard après avoir réparé votre endpoint
Après un succès Compteur remis à zéro Un seul succès efface la série d'échecs

⚠️ Pas de re-livraison automatique : une livraison échouée n'est pas rejouée. Si votre endpoint était indisponible, récupérez les messages manqués depuis votre dashboard Wazzap AI.


Exemples de serveurs webhook

Tester rapidement sans coder

Pour valider votre configuration sans écrire de serveur, utilisez un service d'inspection de webhooks (webhook.site, webhooklens.cloud, requestbin…) : collez l'URL générée dans le dashboard, envoyez un message WhatsApp à votre agent depuis un autre téléphone, et observez la livraison arriver.

Transférer vers Slack

app.post('/wazzap-webhook', async (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();
  res.status(200).json({ ok: true });

  const { event, data } = req.body;
  if (event !== 'message.received') return;

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `💬 *${data.contactName || data.phoneNumber}* : ${data.message}`
    })
  });
});

Répondre automatiquement via l'API

app.post('/wazzap-webhook', async (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();
  res.status(200).json({ ok: true });

  const { event, data } = req.body;
  if (event !== 'message.received') return;

  // Exemple : accusé de réception hors horaires d'ouverture
  const hour = new Date().getHours();
  if (hour < 9 || hour >= 18) {
    await fetch('https://api21.wazzap.ai/api/wazzap/send-message', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.WAZZAP_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phoneNumber: '+' + data.phoneNumber,
        message: 'Merci pour votre message ! Nous vous répondrons dès 9h.',
        disableAiResponse: true
      })
    });
  }
});

Bonnes pratiques

1. Répondez vite, traitez après

Votre endpoint a 10 secondes pour répondre. Accusez réception immédiatement (200) puis traitez le message en asynchrone (queue, worker…). Un traitement synchrone lent = timeouts = webhook désactivé au bout de 20 échecs.

2. Vérifiez toujours la signature

Sans vérification HMAC, n'importe qui connaissant votre URL peut vous injecter de faux messages.

3. Soyez idempotent

Utilisez data.messageId comme clé d'idempotence : si vous recevez deux fois le même messageId, ne traitez qu'une seule fois.

4. Ignorez les événements inconnus

Routez sur le champ event et répondez 200 aux types que vous ne gérez pas — de nouveaux types pourront apparaître.

5. Surveillez votre endpoint

Un endpoint qui tombe silencieusement = webhook désactivé après 20 échecs. Monitorez sa disponibilité, et vérifiez régulièrement le statut du webhook dans le dashboard.


Dépannage

Je ne reçois aucun webhook :

  1. Vérifiez que le webhook est bien activé dans le dashboard (il a pu être désactivé après 20 échecs)
  2. Rappelez-vous que seuls les messages entrants déclenchent le webhook — pas vos envois API ni vos messages tapés depuis le téléphone
  3. Vérifiez que votre agent WhatsApp est bien connecté
  4. Testez avec un service d'inspection (webhook.site) pour isoler le problème côté Wazzap ou côté serveur

L'URL est refusée à l'enregistrement :

  1. L'URL doit être en https:// et publiquement accessible (pas de localhost/IP privée)
  2. Votre serveur doit répondre 2xx au ping webhook.test en moins de 10 s

La vérification de signature échoue :

  1. Calculez le HMAC sur le corps brut de la requête, pas sur le JSON re-sérialisé
  2. Vérifiez que vous utilisez le bon secret (ré-affichable dans le dashboard)
  3. Le header est au format sha256=<hex> — comparez la chaîne complète

Contact

Pour toute question :

  • Consultez d'abord cette documentation
  • Contactez nous à l'adresse contact@Wazzap.ai

APIWebhooksWhatsAppIntégrationDéveloppement

Crée votre assistant IA gratuitement

Transformez vos conversations WhatsApp en opportunités commerciales, qualifiez vos leads et prenez des rendez-vous automatiquement

Amadou Fall
Kaaramoo
Bestilia
Joel Toulan
4.8 / 5
Utilisé par + 40 125 utilisateurs

Aucune carte de crédit requise ·

Documentation API Wazzap AI - Webhooks | Wazzap AI - Le 1er Setter IA pour les agences.