Comment créer un bot de génération de leads WhatsApp avec des webhooks
21 mai 2026
·
13 min de lecture
Ce que vous allez construire : Bot de génération de leads WhatsApp entrant · Machine à états de conversation (Redis) · Scoring de qualification GPT-4o · Écriture via API CRM · Alerte de transfert Slack · Alternative no-code n8n · Code Node.js complet tout au long
Ce que vous allez construire dans ce tutoriel
Bot WhatsApp entrant qui qualifie les leads à travers la conversation
Machine à états soutenue par Redis suivant l'étape de chaque contact
GPT-4o notant les leads de 1 à 10 avec sortie JSON
Écriture CRM HubSpot/Pipedrive lors de la qualification
Alerte Slack pour transfert humain sur les leads à score élevé
Alternative no-code n8n pour l'ensemble du flux
Inbound d'abord : pourquoi cette architecture surpasse les bots outbound
Chaque tutoriel no-code de chatbot WhatsApp se rabat par défaut sur l'outbound — modèles, BotPress, Make.com, envoi de messages à des listes. C'est la voie difficile. Meta exige l'approbation des modèles (1 à 5 jours ouvrés), facture chaque message sortant et bannit les numéros qui envoient du contenu signalé comme spam. La plupart des tutoriels passent rapidement sur cela, puis vous le découvrez à 23 h quand votre numéro est restreint.
La meilleure architecture, c'est inbound d'abord :
Le client vous envoie un message en premier (depuis une publicité Click-to-WhatsApp, un lien WhatsApp ou un QR code)
Cela ouvre une fenêtre de service gratuite de 24 heures — toutes vos réponses sont gratuites
Aucune approbation de modèle nécessaire pour toute réponse dans cette fenêtre
Votre webhook se déclenche, votre bot prend le relais, les leads se qualifient eux-mêmes
Ce n'est pas une stratégie passive. Vous générez du trafic entrant avec des publicités payantes — Click-to-WhatsApp sur Facebook et Instagram envoie les gens directement dans une conversation WhatsApp avec votre numéro. Meta vous offre une fenêtre gratuite de 72 heures pour ces conversations (pas seulement 24). Vous payez pour le clic publicitaire. La conversation de qualification est gratuite.
Vue d'ensemble de l'architecture
Cinq composants. Tous sont remplaçables — échangez HubSpot contre Salesforce, GPT-4o contre Claude 3, Redis contre DynamoDB. L'architecture est la constante ; les fournisseurs sont optionnels.
Architecture
pipeline.txt
Le client envoie un message WhatsApp
↓
[WhatsApp Cloud API] — déclenche un webhook HTTP POST
↓
[SocialHook] — vérifie HMAC, normalise le payload, livraison <50ms
↓
[Votre serveur webhook — Node.js / Express]
1. Charger l'état de conversation depuis Redis (par numéro de téléphone)
2. Faire avancer la machine à états : poser la prochaine question de qualification
3. OU : appeler GPT-4o pour le scoring final
4. Répondre via l'endpoint Graph de Cloud API
5. Sauvegarder l'état mis à jour dans Redis
↓
[Redis] — stockage de l'état de conversation (TTL : 48 h par contact)
↓
À QUALIFIED (score ≥ 7) :
→ POST vers HubSpot / Pipedrive CRM API
→ POST vers webhook Slack (alerte équipe commerciale)
→ Réponse au client : « Un spécialiste vous contactera dans 2 h »
À DISQUALIFIED (score < 4) :
→ Enregistrer en base de données
→ Envoyer un lien de ressource utile
→ Fermer la conversation
Étape 1 : mettre en route la couche webhook
Avant d'écrire la logique du bot, vous devez recevoir les messages WhatsApp entrants sur votre serveur sous forme de JSON propre. Le chemin le plus rapide : connectez votre numéro WhatsApp à SocialHook, collez l'URL de votre serveur comme destination, et chaque message entrant arrive en payload normalisé en moins de 50 ms.
Si vous préférez les webhooks directs de Cloud API, suivez le guide de configuration complet. Pour ce tutoriel, nous utiliserons le format normalisé de SocialHook qui élimine le besoin de code de vérification HMAC et d'extraction de payload imbriqué dans votre handler de bot.
Le payload que votre handler de bot reçoit pour chaque message client entrant :
JSON
inbound-event.json (SocialHook normalized)
{
"platform": "whatsapp",
"event": "message.received",
"timestamp": 1747231892,
"from": "+1 555 000 1234", // E.164 — la clé de la machine à états"conversation_id": "conv_8j3k...",
"message": {
"type": "text",
"body": "Hi, I want to know more about your product",
"id": "wamid.HBgL..."
},
"signature_verified": true
}
Étape 2 : concevoir la machine à états de conversation
La machine à états est le cœur du bot. Chaque message WhatsApp est un événement HTTP sans état — votre serveur n'a aucune mémoire des messages précédents sauf si vous la stockez. Redis vous offre un stockage clé-valeur rapide basé sur TTL par numéro de téléphone. L'objet d'état suit à la fois à quelle étape en est la conversation et quelles données ont été collectées jusqu'ici.
Voici le flux complet de qualification avec 7 états :
ÉtatMessage envoyé par le botAvance vers
GREETING
Bonjour ! 👋 Merci d'avoir contacté [Entreprise]. Je suis là pour vous aider à trouver la bonne solution. Comment vous appelez-vous ?
→ COLLECT_NAME
COLLECT_NAME
Ravi de vous rencontrer, [prénom] ! De quelle entreprise venez-vous ?
→ COLLECT_COMPANY
COLLECT_COMPANY
Super. Qu'essayez-vous de résoudre — quel est le principal problème qui vous amène ici aujourd'hui ?
→ COLLECT_USE_CASE
COLLECT_USE_CASE
Compris. À peu près combien de messages ou de conversations gérez-vous par mois ?
→ COLLECT_VOLUME
COLLECT_VOLUME
Et quel est votre budget mensuel approximatif pour ce type d'outil ?
→ COLLECT_BUDGET
COLLECT_BUDGET
Merci — accordez-moi un instant pendant que je rassemble les bonnes informations pour vous. ⏳
→ QUALIFYING (GPT-4o)
QUALIFIED (score ≥ 7)
Vous correspondez parfaitement à ce que nous proposons. Un spécialiste vous contactera dans les 2 heures. Puis-je confirmer votre adresse e-mail ?
→ Écriture CRM + Slack
DISQUALIFIED (score < 4)
Merci [prénom] ! D'après vos besoins, voici une ressource qui pourrait aider : [lien]. N'hésitez pas à nous recontacter quand le moment sera venu.
→ Log + fermer
À quoi ressemble la conversation du point de vue du client :
🤖
YourCompany Bot
● en ligne
État : GREETING
Bonjour ! 👋 Merci de nous contacter. Je suis là pour vous aider à trouver la bonne solution. Comment vous appelez-vous ?
Bonjour, je suis Sarah
État : COLLECT_COMPANY
Ravi de vous rencontrer, Sarah ! De quelle entreprise venez-vous ?
Acme Corp, nous sommes une marque e-commerce qui traite environ 50 000 commandes par mois
État : COLLECT_USE_CASE
Super. Qu'essayez-vous de résoudre — quel est le principal problème qui vous amène ici aujourd'hui ?
Nous croulons sous les tickets de support WhatsApp. Nous devons automatiser le statut des commandes et les retours
État : QUALIFYING → scoring GPT-4o
Merci — accordez-moi un instant pendant que je rassemble les bonnes informations pour vous. ⏳
Vous correspondez parfaitement à ce que nous proposons, Sarah. Un spécialiste vous contactera dans les 2 heures pour discuter de votre configuration spécifique. Puis-je confirmer votre e-mail pour le suivi ?
Étape 3 : construire la machine à états en Node.js
Le handler de webhook charge l'état depuis Redis, exécute la logique de la machine à états, répond via Cloud API et sauvegarde le nouvel état. Voici l'implémentation complète, de qualité production :
Node.js + Express + Redis
leadBot.js
const express = require('express');
const redis = require('redis');
const { sendWhatsApp } = require('./whatsapp'); // voir ci-dessousconst { scoreLead } = require('./scorer'); // scorer GPT-4oconst { writeCRM, notifySlack } = require('./integrations');
const app = express();
const store = redis.createClient({ url: process.env.REDIS_URL });
await store.connect();
app.use(express.json());
// SocialHook envoie les événements normalisés ici
app.post('/webhook', async (req, res) => {
res.sendStatus(200); // accusé de réception immédiatconst event = req.body;
if (event.event !== 'message.received') return;
if (event.message.type !== 'text') return;
const phone = event.from;
const text = event.message.body.trim();
// Charger l'état — par défaut GREETING si premier contactconst raw = await store.get(`lead:${phone}`);
const state = raw ? JSON.parse(raw) : { stage: 'GREETING', data: {} };
const next = awaitadvance(phone, text, state);
// Sauvegarder l'état mis à jour avec TTL de 48hawait store.set(
`lead:${phone}`,
JSON.stringify(next),
{ EX: 172800 } // 48 heures
);
});
async functionadvance(phone, text, state) {
switch (state.stage) {
case'GREETING':
awaitsendWhatsApp(phone,
"Hey! 👋 Thanks for reaching out. What's your name?"
);
return { stage: 'COLLECT_NAME', data: {} };
case'COLLECT_NAME':
awaitsendWhatsApp(phone,
`Nice to meet you, ${text}! What company are you from?`
);
return { stage: 'COLLECT_COMPANY', data: { name: text } };
case'COLLECT_COMPANY':
awaitsendWhatsApp(phone,
"What's the main pain point bringing you here today?"
);
return {
stage: 'COLLECT_USE_CASE',
data: { ...state.data, company: text }
};
case'COLLECT_USE_CASE':
awaitsendWhatsApp(phone,
"How many conversations do you handle per month roughly?"
);
return {
stage: 'COLLECT_VOLUME',
data: { ...state.data, useCase: text }
};
case'COLLECT_VOLUME':
awaitsendWhatsApp(phone,
"And your approximate monthly budget for this?"
);
return {
stage: 'COLLECT_BUDGET',
data: { ...state.data, volume: text }
};
case'COLLECT_BUDGET': {
awaitsendWhatsApp(phone,
"Thanks — just a moment while I put together the right info for you. ⏳"
);
const leadData = { ...state.data, budget: text };
// Scoring asynchrone — ne pas bloquer, l'appelant gère l'étatqualifyLead(phone, leadData); // fire-and-forget avec sa propre gestion d'erreursreturn { stage: 'QUALIFYING', data: leadData };
}
case'COLLECT_EMAIL': {
// Étape finale après QUALIFIED — récolter l'e-mail et fermerconst finalData = { ...state.data, email: text };
awaitwriteCRM(phone, finalData);
awaitsendWhatsApp(phone,
`Perfect, ${finalData.name}! You're all set. Talk soon. 🚀`
);
return { stage: 'DONE', data: finalData };
}
default:
return state; // QUALIFYING / DONE / DISQUALIFIED — ignorer les messages suivants
}
}
app.listen(3000);
Étape 4 : scoring de qualification de leads GPT-4o
Une fois toutes les données de qualification collectées, vous les envoyez à GPT-4o avec un prompt structuré. Le modèle renvoie un objet JSON de scoring — pas de parsing regex, pas de devinette. La clé est d'indiquer au modèle de ne renvoyer que du JSON et rien d'autre.
7 – 10
Lead chaud
Transfert humain immédiat. Alerte Slack vers le commercial. Affaire CRM créée en haute priorité. Client informé que le spécialiste contacte dans 2 h.
4 – 6
Lead tiède
Contact CRM créé en priorité moyenne. Séquence e-mail automatisée déclenchée. Client envoyé ressources produit et étude de cas.
1 – 3
Pas adapté
Enregistré mais sans affaire CRM. Ressource utile envoyée au client. Conversation fermée avec message de sortie courtois.
Node.js + OpenAI
scorer.js
const OpenAI = require('openai');
const oai = newOpenAI({ apiKey: process.env.OPENAI_API_KEY });
async functionscoreLead(leadData) {
const prompt = `You are a B2B lead qualification expert.
Score this lead from 1 to 10 based on fit and intent.
Return ONLY a JSON object — no preamble, no markdown.
Lead data:
- Name: ${leadData.name}
- Company: ${leadData.company}
- Use case: ${leadData.useCase}
- Volume: ${leadData.volume} conversations/month
- Budget: ${leadData.budget}/month
Our product: WhatsApp webhook infrastructure for developers.
Ideal customer: technical team, 500+ convos/month, budget $50–$500.
Return this exact JSON shape:
{
"score": <1-10 integer>,
"rationale": "<2 sentence explanation>",
"action": "handover" | "nurture" | "disqualify",
"priority": "high" | "medium" | "low"
}`;
const response = await oai.chat.completions.create({
model: 'gpt-4o',
max_tokens: 200,
temperature: 0.2, // temp basse = scoring cohérent
messages: [{ role: 'user', content: prompt }],
response_format: { type: 'json_object' } // forcer la sortie JSON
});
returnJSON.parse(response.choices[0].message.content);
}
// Appelé après COLLECT_BUDGET — flux asynchrone completasync functionqualifyLead(phone, leadData) {
try {
const result = awaitscoreLead(leadData);
if (result.action === 'handover') {
awaitsendWhatsApp(phone,
`You're a great fit, ${leadData.name}! A specialist reaches out in 2h. What's your email?`
);
await store.set(`lead:${phone}`, JSON.stringify({
stage: 'COLLECT_EMAIL', data: { ...leadData, score: result.score }
}), { EX: 172800 });
awaitnotifySlack(phone, leadData, result);
} else if (result.action === 'nurture') {
awaitsendWhatsApp(phone,
`Thanks ${leadData.name}! Here's a case study that fits your situation: [link]`
);
} else {
awaitsendWhatsApp(phone,
`Thanks for reaching out! We might not be the best fit right now, but here's a resource: [link]`
);
}
} catch (err) {
console.error('Scorer error:', err);
// Fallback : router vers un humain de toute façonawaitsendWhatsApp(phone, "Let me connect you with our team. One moment!");
awaitnotifySlack(phone, leadData, { score: 'ERROR', rationale: err.message });
}
}
module.exports = { scoreLead, qualifyLead };
Étape 5 : écrire les leads qualifiés dans votre CRM
Une fois scoré, vous écrivez le lead dans HubSpot (ou Pipedrive, Salesforce — changez l'endpoint). Le numéro de téléphone WhatsApp devient l'identifiant du contact. Incluez le résumé de la conversation pour que le commercial ait le contexte complet quand il appelle.
Étape 6 : envoyer des réponses WhatsApp depuis votre serveur
Votre bot envoie des réponses via l'endpoint /messages de WhatsApp Cloud API. C'est le côté sortant — utilisez votre token d'accès permanent System User et votre Phone Number ID.
Format du numéro de téléphone : SocialHook délivre from au format E.164 avec le préfixe + (+15550001234). Le champ to de Cloud API attend aussi E.164. Utilisez event.from directement comme valeur to — aucune transformation requise avec le format normalisé de SocialHook.
Alternative no-code : le workflow n8n
Si vous voulez le même bot sans écrire de Node.js, vous pouvez le construire dans n8n. L'architecture est identique — SocialHook livre le webhook à n8n, et n8n gère la logique d'état grâce à ses code nodes et son intégration Redis.
n8n Workflow
n8n-lead-gen-workflow.txt
Workflow n8n : Bot de génération de leads WhatsApp──────────────────────────────────────
1. Webhook Trigger
→ Méthode : POST
→ Path : /whatsapp-lead
→ URL : collez-la dans la destination SocialHook
2. Code Node — Charger l'état
→ Redis : GET lead:{{ $json.from }}
→ Parser JSON ou par défaut { stage: 'GREETING', data: {} }
3. Switch Node — Ramifier selon state.stage
→ GREETING → Branche A
→ COLLECT_NAME → Branche B
→ COLLECT_* → Branches C–E
→ COLLECT_BUDGET → Branche F (déclenche le scoring)
→ COLLECT_EMAIL → Branche G (écriture CRM finale)
4. HTTP Request Node (par branche)
→ POST vers graph.facebook.com/v21.0/${PHONE_ID}/messages
→ Body : { messaging_product, to, type: 'text', text: { body } }
→ Auth : header Bearer token
5. Code Node — Mettre à jour l'état
→ Redis : SET lead:{{ $json.from }} {{ JSON.stringify(newState) }} EX 172800
6. Sur la branche COLLECT_BUDGET :
→ OpenAI Node : Chat Completions (gpt-4o)
→ Message : prompt de scoring avec les données du lead
→ Parser la réponse JSON : score, action
7. Switch Node — Ramifier selon action
→ handover : → Slack Node + Créer contact HubSpot
→ nurture : → HTTP Request (envoyer message ressource)
→ disqualify : → HTTP Request (message de sortie courtois)
Dépendances : intégration Redis, intégration OpenAI,intégration HubSpot — toutes disponibles nativement dans n8n.
SocialHook + n8n : Configurez SocialHook pour transférer les événements webhook vers votre URL webhook n8n. SocialHook gère la vérification HMAC de Meta et la normalisation du payload — n8n reçoit un body JSON propre sans aucun pré-traitement. Cela fonctionne que vous auto-hébergiez n8n ou utilisiez n8n Cloud. Consultez le guide d'intégration SocialHook avec n8n pour la configuration exacte de la destination HTTP.
Étape 7 : générer du trafic entrant vers votre bot
Un bot webhook sans trafic n'est qu'un serveur qui brûle du calcul. Trois canaux qui génèrent des conversations WhatsApp entrantes sans approbation de modèle requise :
Créez une publicité de génération de leads sur Facebook ou Instagram avec un CTA Click-to-WhatsApp. Lorsque quelqu'un clique, WhatsApp s'ouvre avec votre numéro pré-rempli. Ils envoient un message, votre fenêtre gratuite de 72 heures s'ouvre, votre bot se déclenche. C'est le canal entrant le plus convertissant — vous ciblez par intérêt et intention, et le saut de l'annonce à la conversation se fait en un seul tap. Chaque conversation que votre bot qualifie depuis une publicité CTA est exempte de frais de messages Meta pendant 72 heures.
Lien WhatsApp / QR code
Générez un lien wa.me/+{your_number}?text=Hi. Intégrez-le sur votre site web, votre signature e-mail, votre profil LinkedIn et vos landing pages. Quiconque clique ouvre une conversation WhatsApp pré-remplie vers votre numéro. Une version QR code fonctionne sur les supports imprimés, les événements et le packaging. Zéro coût, zéro configuration au-delà de la génération du lien.
Bouton WhatsApp sur votre site web
Un bouton WhatsApp flottant remplace votre widget de chat en direct — et convertit à des taux nettement plus élevés. Les visiteurs qui cliquent vont directement vers WhatsApp (mobile) ou scannent un QR code (desktop). Votre bot gère la qualification avant qu'un humain ne voie la conversation. Cela remplace un abonnement à un chat en direct coûteux par votre propre infrastructure de qualification pour 50 $/mois au total.
FAQ
Questions fréquentes
Comment fonctionne un bot de génération de leads WhatsApp avec des webhooks ?
Lorsqu'un client envoie un message à votre numéro WhatsApp Business, Cloud API déclenche un HTTP POST vers votre endpoint webhook. Votre serveur lit le message, charge l'état de conversation depuis Redis, pose la prochaine question de qualification, envoie une réponse via Cloud API et sauvegarde l'état mis à jour. Ce cycle se répète à chaque message jusqu'à ce que le lead soit scoré et routé. Voyez le flux complet dans la section architecture ci-dessus.
Puis-je construire cela sans BSP ?
Oui. Enregistrez votre numéro WhatsApp directement via le Meta Developer Portal (gratuit, prend 30 minutes). Utilisez SocialHook comme couche de livraison webhook — il gère la vérification HMAC et la normalisation du payload. Vous appelez Cloud API directement pour les réponses sortantes. Aucun BSP requis, aucun frais de plateforme BSP. Guide complet : obtenez l'accès API sans BSP.
Pourquoi Redis pour l'état de conversation plutôt qu'une base de données ?
Redis est en mémoire, lectures en sub-milliseconde, et supporte nativement l'expiration de clé basée sur TTL. Votre handler webhook doit charger l'état de conversation à chaque message — la latence ici impacte directement la vitesse de réponse. Une requête PostgreSQL ou MongoDB ajoute 5–50 ms par message ; Redis ajoute <1 ms. La fonction TTL gère les conversations abandonnées automatiquement (TTL 48 h signifie que l'état périmé est ramassé sans tâche cron). Pour un faible volume (<100 conversations concurrentes), un Map local ou même une base SQLite fonctionne bien.
Quelle est la différence entre génération de leads WhatsApp inbound et outbound ?
Inbound : le client vous écrit en premier → fenêtre gratuite 24 h (72 h depuis publicités Click-to-WhatsApp) → aucun modèle requis → réponses gratuites dans le quota. Outbound : vous initiez le contact → nécessite un modèle approuvé par Meta → approbation 1–5 jours → facturation par message → risque de bannissement plus élevé. Pour la génération de leads, inbound d'abord est la bonne architecture. Générez du trafic avec des annonces ou des liens, laissez le bot gérer la qualification sur la conversation entrante.
Combien coûte ce bot à exploiter par mois ?
Détail de l'infrastructure : SocialHook (couche webhook) = 50 $/mois forfaitaire. Redis (niveau gratuit Upstash ou 7 $/mois) = ~7 $. OpenAI GPT-4o (200 tokens par appel de scoring × votre volume de leads à ~0,005 $ par appel) = ~5–50 $ selon le volume. HubSpot (niveau CRM gratuit) = 0 $. Cloud API entrant = 0 $ (dans le quota gratuit). Total pour une opération de 500 leads/mois : ~60–100 $/mois. Aucun frais par lead, aucun abonnement BSP, aucune tarification basée sur les contacts.
Comment gérer les messages non textuels (images, notes vocales) dans le bot ?
Filtrez-les dans le handler webhook en vérifiant event.message.type !== 'text' et envoyez une invite douce : « Veuillez envoyer votre réponse en texte pour que je puisse mieux vous aider. » Pour les notes vocales, vous pouvez optionnellement passer event.message.id par l'API Whisper pour la transcription avant traitement — bien que cela ajoute de la latence et du coût. Pour la qualification de leads, le texte seul est le bon point de départ. Voir tous les types de messages expliqués.
Connectez votre numéro WhatsApp à SocialHook et commencez à recevoir des événements JSON normalisés sur votre bot en moins de 5 minutes. La machine à états, le scoring et l'écriture CRM sont à vous de construire — SocialHook s'assure simplement que chaque message entrant arrive propre, vérifié et renvoyé en cas d'échec.
Arrêtez de gérer les API Meta. Commencez à construire.
Connectez votre premier compte Facebook, Instagram ou WhatsApp en moins de 2 minutes. Votre webhook reçoit son premier payload avant que votre café refroidisse.