Aller au contenu principal

Webhook WhatsApp

Recevez les messages WhatsApp sur votre serveur,
depuis le numéro que vous utilisez déjà.

Les webhooks de Meta demandent une app Meta, un portefeuille business et un numéro basculé sur l'API. Reliez plutôt votre WhatsApp existant : chaque message entrant arrive sur votre URL en JSON signé.

Pressé ? Résumez cet article avec une IA

La réponse courte

Peut-on avoir un webhook WhatsApp sans l'API Business de Meta ?

Oui. Unlimited Messaging relie votre numéro WhatsApp existant comme appareil connecté, comme WhatsApp Web, et envoie chaque message reçu à votre URL sous forme d'événement JSON signé selon Standard Webhooks, nommé message.received. Pas d'app Meta à créer, pas de vérification d'entreprise, pas de frais par message. Les webhooks sont inclus dans les offres payantes Medium et Max.

La voie officielle, c'est la WhatsApp Cloud API de Meta. Pour recevoir ses webhooks, vous créez une app Meta avec le cas d'usage WhatsApp, vous y rattachez un portefeuille business et un compte WhatsApp Business, vous répondez à une requête GET de vérification avec hub.challenge, et vous servez un certificat TLS valide : Meta refuse les certificats auto-signés. Un nouveau portefeuille ne peut ensuite écrire qu'à 250 personnes par jour (hors fenêtre de 24 heures) tant qu'il n'est pas vérifié, et une entreprise qui construit cela pour des clients doit en plus passer l'App Review de Meta.

Unlimited Messaging prend l'autre chemin : vous scannez un QR code depuis WhatsApp, Appareils connectés, et le numéro continue de fonctionner sur votre téléphone. Chaque message reçu est enregistré puis poussé vers votre endpoint avec l'expéditeur, le texte, la conversation et, pour une photo ou une note vocale, une mediaUrl qui sert le fichier. Votre réponse repart par POST /message depuis le même numéro : le client voit une conversation normale.

Le compromis, en toute franchise : c'est le protocole non officiel des appareils connectés, pas un produit Meta. Il convient aux réponses et notifications envoyées à des gens qui vous écrivent déjà. Arroser des inconnus fait bannir un numéro, quel que soit l'outil.

Avant de vous lancer : les API WhatsApp gratuites comparées, et comment protéger un numéro relié contre le bannissement.

Illustration : un téléphone affichant une conversation de type WhatsApp et une pastille verte, avec le titre « Webhook WhatsApp, sans app Meta ».

Les événements

Quels événements WhatsApp peut-on recevoir par webhook ?

Dix types d'événements, choisis par endpoint : un endpoint ne reçoit que ce qui figure dans sa liste eventTypes. Voici ceux dont une boîte de réception WhatsApp a besoin.

  1. message.received

    Un message est arrivé, en conversation 1:1 ou en groupe : texte, image, vidéo, document, audio, note vocale (ptt), sticker ou localisation. Émis une seule fois par message ; un média est annoncé une fois son fichier stocké, avec mediaUrl.

  2. message.self_sent

    Vous avez tapé un message sur le téléphone lui-même, hors API. Même forme que message.received avec direction OUT : votre CRM voit les deux côtés de la conversation.

  3. message.reaction, message.edited, message.deleted

    Quelqu'un a réagi avec un emoji (ou l'a retiré), a modifié un message ou l'a supprimé pour tous. Chacun désigne sa cible par externalId, et peut arriver avant le message qu'il modifie.

  4. message.sent et message.status

    Un message envoyé par API a atteint WhatsApp, puis est passé à DELIVERED, READ, FAILED ou UNDELIVERABLE. message.status porte previousStatus pour ignorer un accusé périmé.

  5. account.status_changed

    Le numéro s'est connecté, déconnecté, a été déconnecté de force ou restreint par WhatsApp (BLOCKED, avec blockedUntil). Abonnez-vous-y pour qu'une boîte silencieuse ne passe jamais inaperçue.

POST https://example.com/webhooks/umJSON
{
  "type": "message.received",
  "eventId": "whd_6f1c2a9e-1b7d-4c55-9a43-2f0e8b7d3c11",
  "occurredAt": "2026-11-09T10:00:01.000Z",
  "data": {
    "message": {
      "id": "msg_0e55ff33-854a-482c-8510-68226e09cdcb",
      "externalId": "3EB0A1C2D3E4F5061728",
      "direction": "IN",
      "fromMe": false,
      "content": "Hi! Is my order ready for pickup?",
      "sentAt": "2026-11-09T10:00:00.000Z",
      "receivedAt": "2026-11-09T10:00:01.000Z",
      "sender": "+447700900123",
      "senderId": "[email protected]",
      "senderName": "Lea",
      "participant": null,
      "replyTo": null,
      "transcript": null
    },
    "channel": {
      "type": "WHATSAPP",
      "account": { "id": "acc_373bc415-447a-4813-9b61-a3a09ea9aaf3", "name": "Support" },
      "conversation": {
        "id": "[email protected]",
        "isGroup": false,
        "address": "+447700900123"
      }
    }
  }
}
Un vrai corps message.received, construit d'après le schéma publié. sender est au format +E.164, ou null quand WhatsApp masque le numéro : senderId identifie alors la personne. Les événements n'arrivent pas dans l'ordre : triez sur sentAt.

Mise en place

Comment recevoir les messages WhatsApp entrants en Node.js ?

Trois étapes : relier le numéro, enregistrer un endpoint, vérifier et traiter chaque POST. Répondez en moins de 10 secondes et faites le travail ensuite.

  1. 01

    Reliez votre WhatsApp

    Dans l'application web, ajoutez un compte WhatsApp et scannez le QR code depuis WhatsApp, Paramètres, Appareils connectés. Le numéro reste sur votre téléphone.

  2. 02

    Enregistrez l'endpoint

    POST /webhooks avec votre URL et vos eventTypes, ou ajoutez-le sur la page Webhooks du tableau de bord. Le secret (whsec_...) n'est affiché qu'une fois.

  3. 03

    Vérifiez, acquittez, agissez

    Contrôlez l'en-tête webhook-signature, renvoyez un 2xx, puis dédupliquez sur eventId : la livraison est « au moins une fois ».

POST /webhookscurl
curl https://api.unlimitedmessaging.app/webhooks \
  -H "Authorization: Bearer $UM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/um",
    "eventTypes": ["message.received", "account.status_changed"],
    "description": "Incoming WhatsApp"
  }'

# The response carries "secret": "whsec_..." once. Store it as UM_WEBHOOK_SECRET.
accountIds est facultatif : sans lui, l'endpoint couvre tous vos comptes, y compris les numéros reliés plus tard. PATCH /webhooks/{id} remplace la liste eventTypes.
handler du webhookNode.js / Express
import crypto from 'node:crypto';
import express from 'express';
import { UnlimitedMessagingApiClient } from '@unlimited-messaging/sdk';

const um = new UnlimitedMessagingApiClient({ token: process.env.UM_API_KEY! });
const key = Buffer.from(process.env.UM_WEBHOOK_SECRET!.replace(/^whsec_/, ''), 'base64');
const seen = new Set<string>(); // use your database in production

// Standard Webhooks: HMAC-SHA256 of "id.timestamp.rawBody", base64.
function verify(raw: string, id: string, ts: string, header: string) {
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = crypto.createHmac('sha256', key).update(`${id}.${ts}.${raw}`).digest('base64');
  return header.split(' ').some((entry) => {
    const sig = entry.startsWith('v1,') ? entry.slice(3) : '';
    return sig.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  });
}

const app = express();
app.post('/webhooks/um', express.raw({ type: 'application/json' }), async (req, res) => {
  const raw = req.body.toString('utf8');
  const ok = verify(raw, req.get('webhook-id') ?? '', req.get('webhook-timestamp') ?? '',
    req.get('webhook-signature') ?? '');
  if (!ok) return res.sendStatus(401);
  res.sendStatus(200); // answer within 10 s, work afterwards

  const event = JSON.parse(raw);
  if (seen.has(event.eventId)) return; // at-least-once delivery
  seen.add(event.eventId);

  if (event.type === 'message.received' && event.data.channel.type === 'WHATSAPP') {
    const { id, content } = event.data.message;
    if (content?.toLowerCase().includes('order')) {
      // Answers in the same chat, from the same linked number.
      await um.messages.messageSend({ replyToMessageId: id, text: 'Checking your order now.' });
    }
  }
});
app.listen(3000);
La vérification suit le schéma documenté : HMAC-SHA256 sur id.timestamp.rawBody avec le secret décodé en base64, horodatages de plus de 5 minutes rejetés. Toute bibliothèque Standard Webhooks fait de même. replyToMessageId répond dans la même conversation.

Vibe coding

Vous codez avec Cursor, Claude Code, Lovable ou Bolt ? Collez ceci

Le prompt donne à votre éditeur IA l'endpoint, le schéma de signature et la forme des événements : il écrit la route du webhook au lieu de deviner.

Prompt à copier
Add an Unlimited Messaging webhook to this app so it receives incoming WhatsApp messages from my own number.

1. Create a POST route at /webhooks/um that reads the RAW request body (no JSON parsing before verification).
2. Verify the Standard Webhooks signature: key = base64-decode(UM_WEBHOOK_SECRET without its "whsec_" prefix); expected = base64(HMAC-SHA256(key, `${webhook-id}.${webhook-timestamp}.${rawBody}`)); accept if any space-separated entry of the webhook-signature header equals `v1,${expected}` (constant-time compare); reject timestamps more than 5 minutes off. Return 401 if invalid.
3. Return 200 at once, then process. Deduplicate on body.eventId.
4. On type "message.received" with data.channel.type "WHATSAPP", read data.message.content, data.message.sender (+E164 or null), data.message.senderName and data.message.mediaUrl, and save them.
5. To reply, install @unlimited-messaging/sdk, read UM_API_KEY from env, and call `new UnlimitedMessagingApiClient({ token }).messages.messageSend({ replyToMessageId: data.message.id, text })`.
6. Add UM_API_KEY and UM_WEBHOOK_SECRET to .env and .env.example, never hardcode them.

Docs: https://docs.unlimitedmessaging.app (Webhooks section). Register the URL with POST https://api.unlimitedmessaging.app/webhooks and eventTypes ["message.received"].

Ce que l'on construit

Que faire des messages WhatsApp entrants ?

Tout ce qui commence par « quand un client écrit ». Quelques usages qui collent bien au modèle du numéro relié.

Un répondeur IA sur votre vrai numéro

Passez content à Claude ou GPT, renvoyez la réponse avec replyToMessageId. Vos clients écrivent toujours au numéro qu'ils ont enregistré, et vous reprenez la main depuis le téléphone quand vous voulez.

WhatsApp dans votre CRM ou votre helpdesk

Enregistrez chaque message.received et message.self_sent sur la fiche du contact, par sender ou senderId : toute la conversation vit à côté du deal ou du ticket.

  1. Suivi de commande ou de réservation

    Repérez un mot-clé ou un numéro de commande dans le texte, cherchez-le, répondez en quelques secondes. Les médias arrivent comme fichier stocké à récupérer.

  2. Suivi de groupes

    Dans un groupe, participant indique qui a écrit et channel.conversation.participants liste les membres, mis à jour quand le groupe change.

  3. Alertes d'équipe

    Transférez un message urgent sur Slack ou par email, ou prévenez quelqu'un quand account.status_changed signale une déconnexion.

  4. Flux no-code

    Pointez le webhook vers n8n, Make ou Zapier et aiguillez selon type et channel.type, sans écrire de serveur.

Le tableau

Webhooks WhatsApp entrants : Cloud API de Meta, Twilio et Unlimited Messaging

Meta et Twilio, c'est la plateforme Business officielle, avec ses validations et sa tarification par message. Unlimited Messaging relie le numéro que vous avez déjà.

Webhooks WhatsApp entrants : Cloud API de Meta, Twilio et Unlimited Messaging
Meta Cloud APIOfficiel, en directTwilio WhatsAppOfficiel, via un fournisseurUnlimited MessagingAppareil connecté, offres payantes
À mettre en place d'abordApp Meta, portefeuille business, compte WhatsApp BusinessCompte Twilio, portefeuille Meta Business, WABA créé via TwilioUn scan de QR code depuis votre téléphone
Votre numéro actuelEnregistré sur la plateformeNe doit pas déjà être enregistré sur WhatsAppConservé, toujours utilisable sur votre téléphone
Validation et limites250 destinataires par jour à l'initiative de l'entreprise avant vérification ; App Review pour les tech providersVérification d'entreprise Meta avant la productionAucune côté Meta ; les règles anti-spam de WhatsApp s'appliquent
Poignée de main de l'endpointVérification GET avec hub.challenge, TLS valide, pas d'auto-signéURL de webhook réglée sur l'expéditeurAucune : enregistrez l'URL, recevez
SignatureX-Hub-Signature-256, HMAC-SHA256 avec le secret de l'appX-Twilio-Signature, HMAC-SHA1 avec l'auth tokenStandard Webhooks, HMAC-SHA256 avec un secret par endpoint
Format du corpsJSON, imbriqué entry/changesForm-encoded (application/x-www-form-urlencoded)JSON, même enveloppe pour l'email et le SMS
Nouvelles tentativesFréquence décroissante pendant 7 jours au plusNon précisé dans la doc des webhooks8 essais sur un peu plus de 3 jours
Coût pour recevoir et répondreRéponses de service gratuites jusqu'à 1 000 par numéro et par mois, puis payantes ; modèles payants0,005 $ par message, entrant ou sortant, plus les frais MetaAucun frais par message ; webhooks avec Medium ou Max

Vérifié le 9 novembre 2026 sur la documentation Meta (webhooks, limites d'envoi, tarifs) et sur les pages Twilio (inscription WhatsApp, tarifs, sécurité des webhooks). Liens dans les sources.

Les contreparties

Quelles sont les limites d'un webhook WhatsApp sur numéro relié ?

Se passer de Meta supprime les validations. Pas les règles de WhatsApp, ni les réalités d'un appareil connecté.

  1. Les webhooks sont payants

    L'offre Free envoie et affiche les messages reçus dans l'application web, mais les pousser vers votre serveur demande Medium (19 € par mois, ou 17 € par mois en annuel) ou Max. Aucun frais par message dans les deux cas.

  2. C'est non officiel

    Le protocole des appareils connectés est celui de WhatsApp Web, pas une API Meta. WhatsApp peut restreindre un numéro qui spamme ou se fait signaler : account.status_changed vous prévient, avec blockedUntil quand la date est connue.

  3. Un appareil connecté suit les règles de WhatsApp

    WhatsApp déconnecte les appareils reliés si le téléphone principal reste hors ligne 14 jours. Ouvrez WhatsApp sur le téléphone de temps en temps et le lien tourne même téléphone éteint.

  4. L'ordre et les doublons sont à votre charge

    La livraison est « au moins une fois » et sans ordre garanti : un texte envoyé juste après une photo arrive souvent avant, la photo attendant que son fichier soit stocké. Dédupliquez sur eventId, triez sur sentAt.

  5. L'historique est optionnel

    Seuls les messages reçus après la liaison sont annoncés, sauf si le compte a été relié avec syncHistory : les anciennes conversations arrivent alors en lots message.history de 100 messages au plus (médias décrits, pas stockés).

FAQ

Questions sur les webhooks WhatsApp

Ilian, ingénieur logiciel senior

Écrit par

· Senior Software Engineer

Ingénieur logiciel senior, il fonde et dirige plusieurs sociétés tech. Il conçoit des produits autour des API de messagerie, de l’automatisation et de l’intégration de l’IA, et partage ici ce qu’il apprend en construisant.

Voir le profil LinkedInMis à jour le

Vos messages WhatsApp, sur votre serveur.

Créez un compte, reliez votre numéro par QR code et activez les webhooks avec Medium ou Max. L'envoi reste gratuit sur toutes les offres.