Créer votre support professionnel avec WhatsApp Business
Le client veut envoyer un message. Votre équipe a besoin d'un ticket. Ce n'est pas le même besoin, et c'est pour cela qu'il faut deux outils.
WhatsApp est, dans une grande partie du monde, le canal par lequel les gens écrivent aux entreprises. Pas parce qu'il est meilleur : parce qu'il est déjà installé, déjà ouvert, et qu'il ne demande rien. Ni compte à créer, ni mot de passe, ni application à télécharger, ni « votre demande a bien été enregistrée sous le numéro… ».
Et pourtant, une équipe de support qui essaie de travailler dans WhatsApp s'arrête au bout de quelques semaines. Ce texte explique pourquoi les deux besoins sont incompatibles dans un seul outil, pourquoi les mélanger fonctionne très bien, et comment le faire concrètement — code compris.
Deux besoins qui ne sont pas le même
La confusion vient de ce qu'on appelle « support » deux choses différentes selon le côté du guichet.
| Ce que veut le client | Ce dont l'équipe a besoin | |
|---|---|---|
| Identité | Aucune. Il écrit, point | Savoir qui écrit, et son historique |
| Trace | Sa conversation, dans son téléphone | Tout, cherchable, exportable, pendant des années |
| Délai | Une réponse | Un engagement mesuré, avec des compteurs |
| Répartition | Peu importe qui répond | Savoir qui doit répondre, et le voir |
| Priorité | La sienne est toujours la plus haute | Un ordre, décidé par des règles |
| Contexte | Il a déjà tout dit | La commande, la facture, les tickets précédents |
Un outil qui satisferait la colonne de gauche est une messagerie. Un outil qui satisferait celle de droite est un système de ticketing. Ce sont deux produits, et vouloir un seul des deux fait perdre l'autre.
Pourquoi travailler dans WhatsApp s'arrête vite
L'application WhatsApp Business est excellente pour une personne seule ou deux. Voici les murs, dans l'ordre où on les rencontre :
- 1Il n'y a pas d'assignation. Deux agents ouvrent la même conversation, répondent tous les deux, ou aucun. Rien ne dit « c'est à moi ». C'est le premier mur, et il arrive au troisième agent.
- 2Il n'y a pas de statut. Une conversation lue n'est pas une conversation traitée. Rien ne distingue « j'attends le client » de « j'ai oublié ».
- 3L'historique appartient à l'appareil. Un agent qui part emporte le contexte. Un téléphone perdu emporte des conversations client — ce qui est aussi un problème de données personnelles, pas seulement d'organisation.
- 4Aucun compteur. Combien de demandes hier ? Quel délai moyen ? Quelle proportion sur les livraisons ? Aucune réponse, et donc aucun pilotage.
- 5Aucune mémoire du client. Ce monsieur a écrit trois fois ce mois-ci pour le même problème : personne ne le voit, parce que rien ne relie les conversations entre elles.
- 6Aucune trace d'audit. Qui a vu quoi, quand. Le jour où un client demande la suppression de ses données, la réponse est une fouille manuelle dans des téléphones.
Aucun de ces six points n'est un défaut de WhatsApp. WhatsApp est une messagerie, et une messagerie n'a aucune raison d'avoir des SLA. Le mur n'est pas le produit, c'est l'usage qu'on lui demande.
Pourquoi mettre le ticketing derrière, et pas devant
La bonne architecture est asymétrique, et c'est ce qui la rend efficace : le client garde la simplicité, l'équipe récupère la structure. Personne ne renonce à rien.
Concrètement, ce que le passage par un système de ticketing ajoute à un message WhatsApp :
- Un numéro et un statut. Le message devient une chose qui peut être en cours, en attente, résolue — et qu'on peut donc compter.
- Un SLA qui court. Avec ses heures ouvrées, ses jours fériés et sa mise en pause. Voir les huit pièges de ce calcul, parce qu'ils s'appliquent tous ici.
- Des règles. « Message contenant *remboursement* → équipe facturation, priorité haute. » Voir les règles et SLA.
- Un contact unifié. Le même monsieur qui écrit par WhatsApp aujourd'hui et par email la semaine prochaine est une seule fiche, avec un seul historique. C'est ce que la messagerie ne peut structurellement pas faire.
- Un assistant. Triage à l'arrivée, brouillon de réponse tiré de votre base de connaissances, résumé d'un fil de trente messages. Voir l'assistant.
- Le mobile pour l'agent. Traiter une demande WhatsApp depuis un téléphone, mais dans un outil qui sait à qui elle est assignée — voir l'application mobile.
Là où l'IA change vraiment quelque chose sur ce canal
WhatsApp a une propriété que l'email n'a pas : les messages sont courts, nombreux, et arrivent en rafale. « bonjour », « j'ai un souci », « avec ma commande », « la 4471 » : quatre messages en vingt secondes pour une seule demande.
Cela crée deux problèmes que l'assistant traite bien, et il faut les distinguer :
- 1Le regroupement. Quatre messages ne sont pas quatre tickets. C'est votre pont qui décide — voir le code plus bas — mais c'est l'assistant qui rend le résultat lisible : un résumé en une phrase de ce que la rafale demandait.
- 2Le triage sans sujet. Un email a un objet ; un message WhatsApp n'en a pas. Sans triage automatique, toutes vos demandes WhatsApp arrivent sans catégorie et sans priorité, donc dans le désordre. Le triage à l'arrivée donne une catégorie, une priorité et une langue — et l'agent voit ce qui a été proposé plutôt qu'un champ rempli en silence.
Sur le coût, l'ordre de grandeur est publié : un triage vaut environ 0,00023 € et un brouillon de réponse 0,00057 €. Nous publions le journal d'appels complet parce que ce sont les seuls chiffres qui permettent de décider si l'IA est rentable sur un canal à fort volume comme celui-ci. Elle l'est : mille demandes WhatsApp par mois coûtent moins de deux euros d'inférence.
La fenêtre de 24 heures, et pourquoi elle change votre organisation
C'est la contrainte la plus importante de la plateforme WhatsApp Business, et celle qu'aucun article sur le sujet ne relie à l'organisation du support : vous pouvez répondre librement dans les 24 heures suivant le dernier message du client. Passé ce délai, vous ne pouvez plus écrire ce que vous voulez — il faut passer par un gabarit approuvé au préalable.
Trois conséquences très concrètes :
- Un SLA de résolution à 72 heures est incompatible avec ce canal si votre réponse finale arrive hors fenêtre. Vous aurez tenu votre engagement et vous ne pourrez pas le dire au client. Il faut donc soit un engagement plus court sur WhatsApp, soit un message d'attente dans la fenêtre pour la relancer.
- Le « je vous reviens demain » devient une action technique, pas une politesse : il rouvre la fenêtre.
- Vos gabarits doivent être préparés avant d'en avoir besoin. Leur approbation prend du temps, et le jour où vous en avez besoin, c'est déjà trop tard. Prévoyez-en au moins deux : « nous avons une réponse » et « nous avons besoin d'une information ».
C'est le genre de règle qui doit être visible dans l'outil de l'agent et non dans un document d'intégration. Aujourd'hui, avec un pont, elle ne l'est pas : c'est la première chose qu'un canal natif apporterait.
Le pont, concrètement
Deux directions à câbler, et la seconde est celle qu'on oublie.
Sens entrant : WhatsApp → ticket
Le webhook de la plateforme WhatsApp appelle votre service à chaque message. Votre service décide : nouvelle demande, ou message d'une demande en cours ?
// Le webhook WhatsApp vous donne un numéro (wa_id), pas une adresse email.
// Notre API exige requester_email : on fabrique donc une adresse
// DÉTERMINISTE par numéro. Ce n'est pas un pis-aller — c'est aussi ce qui
// permettra au sens sortant de reconnaître un ticket WhatsApp (voir plus bas).
const requester = `${waId}@wa.exemple.fr`;
// Une conversation en cours ? L'API filtre par requester_id ou par tag, PAS
// par email : le plus simple est que votre pont garde sa propre table
// numéro -> numéro de ticket actif. Elle sert aussi au sens retour.
const active = await store.ticketFor(waId);
if (active) {
await ohd(`/api/v1/tickets/${active}/messages`, {
method: "POST",
// internal: false => le message est écrit en public_reply, donc visible
// du client dans le fil. internal: true en ferait une note interne.
body: { body: text, internal: false },
});
} else {
// Nouvelle demande. Le sujet n'existe pas sur WhatsApp : on prend les
// premiers mots, le triage de l'assistant corrigera la catégorie.
const t = await ohd("/api/v1/tickets", {
method: "POST",
body: {
requester_email: requester,
requester_name: profileName ?? waId,
subject: text.slice(0, 60),
message: text,
tags: ["whatsapp"], // indispensable : voir le tableau des manques
},
});
await store.link(waId, t.number);
}Le regroupement des rafales se règle ici, et le plus simple marche bien : attendre quelques secondes avant de créer le ticket, et concaténer ce qui arrive entre-temps. Trois secondes suffisent à recoller « bonjour » et « j'ai un souci ».
Sens sortant : réponse de l'agent → WhatsApp
C'est la moitié qu'on néglige, et sans elle l'agent répond dans le vide. Notre produit émet des webhooks signés sur `message.created` ; votre service les reçoit et appelle l'API d'envoi de WhatsApp. Avec un détail qu'il faut connaître avant d'écrire une ligne : la charge du webhook porte le ticket, pas le message. Elle ne contient donc ni le texte, ni l'indication « note interne », ni les étiquettes — il faut un aller-retour pour lire le fil.
// 1. Vérifier la signature AVANT de lire le corps : un webhook non vérifié
// est une porte ouverte sur votre passerelle d'envoi.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(raw: string, header: string, secret: string) {
const expected = createHmac("sha256", secret).update(raw).digest("hex");
return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
// 2. Le point à connaître : la charge de "message.created" contient LE TICKET,
// pas le message. Elle ne dit donc ni le texte, ni s'il s'agit d'une note
// interne, ni les étiquettes. Il faut aller chercher le fil.
if (event.event === "message.created") {
const wa = phoneFromRequester(event.ticket.requester?.email);
if (!wa) return; // pas un ticket WhatsApp : l'adresse fabriquée sert de marqueur
const { data } = await ohd(`/api/v1/tickets/${event.ticket.number}/messages`);
const last = data.at(-1);
// 3. Et la ligne qui compte plus que tout le reste. Les trois valeurs de
// "kind" sont public_reply, internal_note et system_event : on n'envoie
// QUE la première, et seulement si elle vient d'un agent.
if (last?.kind === "public_reply" && last.author_type === "agent") {
await sendWhatsApp(wa, last.body_text);
}
}Ce qui n'existe pas encore, et ce que ça coûte
Voici ce que le pont ne faisait pas, et ce que le canal natif fait désormais. Cinq lignes sur six sont fermées ; la sixième est écrite telle quelle parce qu'elle reste vraie.
| Manque du pont | Conséquence | Aujourd'hui, avec le canal natif |
|---|---|---|
| WhatsApp n'est pas dans les canaux | Les tickets arrivent en `api` : vos rapports ne distinguent pas WhatsApp du reste de votre trafic d'API | Fermé. `whatsapp` est une valeur du canal : vos rapports le distinguent |
| Pas d'identité par téléphone | L'API exige une adresse email ; le numéro devient une adresse fabriquée | Fermé. Une table de conversations relie le numéro au contact ; l'adresse dérivée reste, mais c'est un fait enregistré et non une supposition |
| La fenêtre de 24 h est invisible | L'agent ne sait pas qu'il ne peut plus écrire librement | Fermé. Le compte à rebours s'affiche au-dessus du champ de réponse, et hors fenêtre l'encart passe au rouge |
| Les gabarits ne sont pas gérés | Hors fenêtre, l'envoi échoue et l'agent croit avoir répondu | Partiellement. Le refus est écrit dans le fil en événement système. L'envoi d'un gabarit, lui, n'existe pas encore — c'est la seule ligne qui reste |
| `message.created` ne porte pas le message | Le pont doit relire le fil à chaque événement pour savoir quoi envoyer, et si c'était une note interne | Sans objet. L'envoi ne passe plus par un webhook : il est branché là où la réponse est écrite |
| Les médias demandent un aller-retour | Une photo envoyée par le client doit être téléchargée puis réattachée | Fermé. Les médias entrants sont téléchargés et attachés au message |
Nous préférons publier ce tableau plutôt qu'une page produit qui laisserait entendre que le canal existe. Il est au plan ; il n'est pas écrit. Le jour où il le sera, les quatre premières lignes disparaissent et la cinquième devient un détail.
Avant de commencer : cinq décisions
- 1Quel numéro. Un numéro déjà utilisé dans l'application WhatsApp Business doit être migré vers la plateforme, et il quitte alors l'application. On ne peut pas avoir les deux sur le même numéro : décidez-le avant de communiquer le numéro à vos clients.
- 2Quel engagement de délai sur ce canal. WhatsApp crée une attente d'immédiateté que l'email ne crée pas. Un SLA copié de l'email produira des clients déçus par un engagement tenu.
- 3Qui répond. Une équipe dédiée, ou tout le monde ? Le volume WhatsApp est plus élevé et plus haché que l'email, et il déséquilibre une file commune.
- 4Ce que l'assistant fait, et ne fait pas. Notre conseil : triage et brouillon, oui ; réponse autonome, non — pas sur ce canal.
- 5Comment vous sortez. Vos conversations vivent chez Meta. Votre historique de tickets, lui, est chez vous et exportable. C'est un argument de plus pour que le ticketing soit le système de référence et la messagerie un simple canal.
La messagerie est là où le client vous parle. Le ticketing est là où votre entreprise s'en souvient. Confondre les deux, c'est perdre l'un des deux.
La configuration du canal tient dans un écran — voir les canaux — et tout ce que fait l'écran, l'API le fait aussi : la documentation. Le code du canal est lisible sous licence AGPL-3.0, vérification de signature et fenêtre de 24 heures comprises : page open source. Il reste une chose à écrire, et c'est celle qui manque à tout le monde : l'envoi d'un gabarit pré-approuvé hors fenêtre.
Nous ne publions pas d'article sans un chiffre mesuré chez nous, du code qu'on peut aller lire, ou une limite avouée. C'est la seule façon d'écrire sur ce métier sans répéter ce que tout le monde écrit déjà.