Webhook multicanal
Chaque réponse, sur chaque canal,
dans un seul webhook.
WhatsApp depuis votre numéro, email depuis votre boîte, SMS depuis votre téléphone : un seul endpoint les reçoit tous dans la même enveloppe JSON signée. Un handler alimente votre boîte de réception, votre agent IA ou votre CRM.
Pressé ? Résumez cet article avec une IA
La réponse courte
Un seul webhook peut-il recevoir WhatsApp, email et SMS ?
Oui. Unlimited Messaging envoie chaque message reçu sur votre numéro WhatsApp relié, votre boîte mail connectée et la SIM de votre téléphone Android vers une seule URL, sous le même événement, message.received, signé selon Standard Webhooks. L'enveloppe ne change jamais ; data.channel.type indique WHATSAPP, EMAIL ou SMS. Les webhooks sont inclus dans les offres payantes Medium et Max.
Assemblée à partir de fournisseurs séparés, la même boîte de réception demande trois intégrations. La Cloud API de Meta envoie du JSON imbriqué signé en X-Hub-Signature-256 après une poignée de main hub.challenge. Twilio envoie SMS et WhatsApp en paramètres form-encoded signés en HMAC-SHA1. SendGrid Inbound Parse envoie du multipart/form-data pour un nom d'hôte dont vous avez déplacé le MX. Trois parsers, trois vérifications de signature, trois idées de ce qu'est une conversation.
Ici, les canaux sont des services à côté d'une seule API, et cette API annonce chaque message de la même façon : id, direction, content, sender, sentAt, la conversation (un chat ou un groupe WhatsApp, la racine d'un fil d'emails, un numéro SMS) et le compte qui l'a reçu. L'email ajoute subject, html et attachments ; WhatsApp ajoute les médias, les groupes, les réactions et les modifications. Les règles de livraison sont communes : répondre 2xx en moins de 10 secondes, 8 essais sur un peu plus de 3 jours, dédupliquer sur eventId.
Les réponses repartent des mêmes identités. replyToMessageId répond sur place dans un chat WhatsApp ou un fil d'emails ; une réponse SMS désigne la SIM. Vos clients voient votre numéro et votre adresse, jamais ceux d'un fournisseur.
À lire aussi : envoyer email et WhatsApp en une seule requête API, et réunir la prospection WhatsApp et email dans une seule boîte.

Les événements
À quoi ressemble le payload du webhook multicanal ?
Le même événement message.received sur chaque canal. Ce qui change : data.channel.type et quelques champs propres au canal.
Commun à tous les canaux
type, eventId, occurredAt ; data.message avec id, externalId, direction, fromMe, content, sentAt, receivedAt, sender, senderId ; data.channel avec type, account (id, name) et conversation (id, isGroup, address).
WHATSAPP
Médias (mediaUrl, mediaType, mimeType), localisations, réponses citées (replyTo), groupes (participant, conversation.participants), plus message.reaction, message.edited et message.deleted.
EMAIL
subject, html (non fiable, à nettoyer), attachments (20 par email au plus), conversation.id égal à la racine du fil et replyTo au message auquel on répond.
SMS
Texte uniquement. conversation.id vaut null : regroupez par sender. Messages reçus après la connexion de l'application Android.
Autour des messages
message.sent et message.status suivent ce que vous envoyez sur les trois canaux ; account.status_changed signale un numéro, une boîte ou une SIM qui décroche.
// WhatsApp
{ "type": "message.received", "eventId": "whd_6f1c...", "occurredAt": "2026-11-09T10:00:01.000Z",
"data": {
"message": { "id": "msg_0e55...", "direction": "IN", "content": "Is my order ready?",
"sender": "+447700900123", "senderName": "Lea", "sentAt": "2026-11-09T10:00:00.000Z" },
"channel": { "type": "WHATSAPP", "account": { "id": "acc_373b...", "name": "Support" },
"conversation": { "id": "[email protected]", "isGroup": false,
"address": "+447700900123" } } } }
// Email: same envelope, plus subject, html and attachments
{ "type": "message.received", "eventId": "whd_9a4f...", "occurredAt": "2026-11-09T14:05:12.000Z",
"data": {
"message": { "id": "msg_3f8a...", "direction": "IN", "content": "Signed quote attached.",
"sender": "[email protected]", "subject": "Re: Quote #1042",
"html": "<p>Signed quote attached.</p>", "attachments": [ { "filename": "quote.pdf" } ] },
"channel": { "type": "EMAIL", "account": { "id": "acc_8b2e...", "name": "[email protected]" },
"conversation": { "id": "[email protected]", "isGroup": false,
"address": "[email protected]" } } } }
// SMS: text only, no conversation id
{ "type": "message.received", "eventId": "whd_2b9e...", "occurredAt": "2026-11-09T08:42:06.000Z",
"data": {
"message": { "id": "msg_7d2c...", "direction": "IN", "content": "Running 10 min late, sorry!",
"sender": "+447700900456", "sentAt": "2026-11-09T08:42:03.000Z" },
"channel": { "type": "SMS", "account": { "id": "acc_5e8d...", "name": "Front desk SIM" },
"conversation": { "id": null, "isGroup": false, "address": "+447700900456" } } } }Mise en place
Comment écrire un seul handler pour tous les canaux ?
Reliez les trois canaux une fois, enregistrez un endpoint, et ne différenciez que là où un canal diffère vraiment.
- 01
Reliez vos canaux
Scannez un QR code pour WhatsApp, connectez votre boîte en IMAP/SMTP, installez l'application Android pour le SMS. Chacun devient un compte.
- 02
Enregistrez un seul endpoint
POST /webhooks avec votre URL et vos eventTypes, ou ajoutez-le sur la page Webhooks du tableau de bord. Sans accountIds, il couvre tous les comptes, même futurs.
- 03
Vérifiez une fois, différenciez tard
Une vérification de signature, un upsert sur eventId, une ligne de boîte de réception. Seul l'appel de réponse diffère : replyToMessageId pour WhatsApp et l'email, la SIM pour le SMS.
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", "message.status", "account.status_changed"],
"description": "Every reply, every channel"
}'
# No accountIds: the endpoint covers every WhatsApp number, mailbox and SIM you have,
# including ones linked later. The response carries "secret": "whsec_..." once.// app/api/webhooks/um/route.ts (Next.js App Router)
import crypto from 'node:crypto';
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');
// 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));
});
}
export async function POST(req: Request) {
const raw = await req.text();
const h = req.headers;
if (!verify(raw, h.get('webhook-id') ?? '', h.get('webhook-timestamp') ?? '',
h.get('webhook-signature') ?? '')) {
return new Response('bad signature', { status: 401 });
}
const event = JSON.parse(raw);
if (event.type !== 'message.received') return new Response(null, { status: 200 });
const { message: m, channel } = event.data;
// One inbox row, whatever the channel (upsert on eventId: delivery is at least once).
await inbox.upsert({
eventId: event.eventId,
channel: channel.type, // WHATSAPP | EMAIL | SMS
from: m.sender ?? m.senderId, // +E164 or email address
thread: channel.conversation.id ?? m.sender, // SMS has no conversation id
subject: m.subject ?? null,
text: m.content,
at: m.sentAt, // order on this, not on arrival
});
const answer = await draftReply(m.content); // your LLM call, or null for a human
if (answer) {
if (channel.type === 'SMS') {
await um.messages.messageSend({
to: { phone: m.sender },
channels: [{ channel: 'SMS', accountId: channel.account.id }],
text: answer,
});
} else {
// WhatsApp and email: same chat or same thread, same number or mailbox.
await um.messages.messageSend({ replyToMessageId: m.id, text: answer });
}
}
return new Response(null, { status: 200 }); // within 10 s; queue slow work
}Vibe coding
Vous codez avec Cursor, Claude Code, Lovable ou Bolt ? Collez ceci
Assez de contexte pour que votre éditeur IA construise toute la route de la boîte unifiée en une passe.
Add a unified inbox to this app with Unlimited Messaging: WhatsApp (my number), email (my mailbox) and SMS (my Android phone) all arrive on ONE webhook.
1. Create a POST route at /api/webhooks/um that reads the RAW body.
2. Verify the Standard Webhooks signature: key = base64-decode(UM_WEBHOOK_SECRET without "whsec_"); 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 otherwise.
3. On type "message.received", upsert one row keyed on body.eventId: channel = data.channel.type (WHATSAPP | EMAIL | SMS), from = data.message.sender ?? data.message.senderId, thread = data.channel.conversation.id ?? data.message.sender, subject = data.message.subject, text = data.message.content, at = data.message.sentAt (order on this, never on arrival).
4. Build an inbox page listing threads across channels with a channel badge.
5. Replies: install @unlimited-messaging/sdk, read UM_API_KEY from env. WhatsApp and email: `client.messages.messageSend({ replyToMessageId: data.message.id, text })`. SMS: `client.messages.messageSend({ to: { phone: sender }, channels: [{ channel: 'SMS', accountId: data.channel.account.id }], text })`.
6. Return 200 within 10 seconds; queue slow work. Put UM_API_KEY and UM_WEBHOOK_SECRET in .env and .env.example.
Docs: https://docs.unlimitedmessaging.app (Webhooks). Register with POST https://api.unlimitedmessaging.app/webhooks and eventTypes ["message.received", "message.status", "account.status_changed"].Ce que l'on construit
Que construire avec un seul webhook pour tous les canaux ?
Trois projets qui demandent trois intégrations ailleurs, et un seul handler ici.
Une boîte de support multicanale
Une liste de conversations, un badge par canal, des réponses envoyées depuis votre numéro ou votre adresse dans le même fil. Ajoutez message.status pour afficher distribué et lu quand le canal le signale.
Un répondeur IA
Donnez content à Claude ou GPT avec l'historique de la conversation, renvoyez la réponse sur le canal d'origine, passez la main à un humain sur mot-clé ou en cas de doute.
Synchro CRM
Rattachez chaque message au contact par téléphone ou adresse email, quel que soit le canal, avec sentAt pour la chronologie.
Bascule de canal
Pas de réponse sur WhatsApp au bout d'un jour ? Relancez par email ou SMS ; le même POST /message peut envoyer sur trois canaux à la fois.
Supervision
account.status_changed sur les trois canaux vous dit quand un numéro, une boîte ou un téléphone cesse de relayer.
Flux no-code
Pointez le webhook vers n8n, Make ou Zapier et aiguillez sur data.channel.type.
Le tableau
Une boîte, trois façons : Twilio, une pile maison ou Unlimited Messaging
Ce qu'il faut pour recevoir les réponses WhatsApp, email et SMS dans le même code.
| Twilio + SendGridUn fournisseur, plusieurs produits | Meta + service de parsing + fournisseur SMSFait maison | Unlimited MessagingUn webhook, offres payantes | |
|---|---|---|---|
| Formats de payload | Form-encoded pour SMS et WhatsApp, multipart pour l'email | JSON de Meta, format du fournisseur, multipart ou JSON pour l'email | Une seule enveloppe JSON |
| Vérifications de signature | X-Twilio-Signature (HMAC-SHA1), plus la protection propre à l'endpoint de parsing | X-Hub-Signature-256 pour Meta, une par autre fournisseur | Une seule vérification Standard Webhooks |
| Identités vues par vos clients | Numéros loués chez Twilio, un expéditeur WhatsApp enregistré | Un numéro basculé sur la Cloud API, un numéro SMS loué | Votre numéro, votre boîte et votre SIM |
| Mise en place avant le premier message | Achat de numéro, enregistrement de l'expéditeur WhatsApp, MX | App Meta et portefeuille business, MX, location de numéro | Scan de QR code, mot de passe de la boîte, application Android |
| Frais par message | 0,0083 $ par segment SMS reçu aux États-Unis, 0,005 $ par message WhatsApp plus frais Meta | Frais Meta au-delà de 1 000 réponses de service par numéro et par mois, frais par SMS | Aucun ; webhooks avec Medium ou Max |
| Statut officiel | WhatsApp Business Platform officielle | WhatsApp Business Platform officielle | WhatsApp par le protocole non officiel des appareils connectés |
Vérifié le 9 novembre 2026 sur les pages Twilio (webhooks, tarifs SMS et WhatsApp), la documentation Meta (webhooks, tarifs) et le guide Inbound Parse de SendGrid (liens dans les sources).
Les contreparties
Quelles sont les limites d'un webhook unifié sur vos propres canaux ?
Un seul format supprime le code de colle. Chaque canal garde sa nature.
Les webhooks sont payants
L'envoi sur les trois canaux est gratuit sur toutes les offres, et les messages reçus s'affichent dans la boîte de l'application web. Les pousser vers votre serveur demande Medium (19 € par mois, ou 17 € par mois en annuel) ou Max.
WhatsApp est un appareil connecté
Le numéro est relié comme WhatsApp Web, par un protocole non officiel : le spam le fait restreindre, et WhatsApp déconnecte les appareils reliés si le téléphone principal reste hors ligne 14 jours.
Le SMS demande un téléphone Android
La SIM relaie via l'application Android gratuite (8.0 ou plus récent), qui doit rester allumée et connectée. Texte uniquement, et seulement les SMS reçus après la connexion.
L'email lit la boîte de réception
Boîtes IMAP uniquement pour l'instant (Gmail avec un mot de passe d'application, votre domaine, la plupart des hébergeurs) ; pas encore d'Outlook ni de Microsoft 365. Le courrier classé en spam ou dans d'autres dossiers n'est pas vu.
Tous les champs n'existent pas sur tous les canaux
Le SMS n'a ni identifiant de conversation ni accusé de lecture, l'email n'a pas de réactions, WhatsApp n'a pas d'objet. Codez sur les champs communs et traitez le reste comme optionnel.
FAQ
Questions sur le webhook multicanal
Sources
Sources & références
Pour vérifier par vous-même les informations de cet article :
- Spécification Standard Webhooksstandardwebhooks.com
- Getting started with webhooks (hub.challenge, X-Hub-Signature-256)Meta for Developers
- Tarifs de la WhatsApp Business PlatformMeta for Developers
- Incoming message webhook request (form-encoded)Twilio Docs
- Webhooks security (X-Twilio-Signature, HMAC-SHA1)Twilio Docs
- Tarifs SMS aux États-UnisTwilio
- Tarifs WhatsApp (0,005 $ par message, entrant ou sortant)Twilio
- Setting up the Inbound Parse webhook (MX, multipart/form-data)Twilio SendGrid Docs
- À propos des appareils connectés (déconnexion après 14 jours sans le téléphone principal)Pages d'aide WhatsApp
- Référence de l'API Unlimited Messaging - WebhooksUnlimited Messaging

Écrit par
Ilian · 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.
Trois canaux, un seul handler.
Créez un compte, reliez votre WhatsApp, votre boîte mail et votre téléphone Android, et activez les webhooks avec Medium ou Max. L'envoi reste gratuit sur toutes les offres.