Au Bénin, au Sénégal ou en Côte d'Ivoire, le client ne remplit pas un formulaire de contact. Il écrit sur WhatsApp. Il envoie une photo, un vocal, trois messages à la suite, à 22 h. Et il attend une réponse dans les minutes qui suivent, sinon il écrit à la boutique d'à côté.
C'est pour ça que le chatbot WhatsApp est le produit IA qu'on me demande le plus souvent de construire, très loin devant tout le reste. Pas un agent qui code, pas un générateur de vidéos. Un bot qui répond aux clients d'une boutique de Cotonou, d'une pharmacie de Dakar, d'une agence de voyage d'Abidjan, en français, tout de suite.
Ce guide te montre comment créer un chatbot WhatsApp de zéro. À la fin tu auras :
- un numéro WhatsApp Business branché sur l'API Cloud de Meta (sans Twilio, sans intermédiaire)
- un serveur Node.js qui reçoit les messages, les envoie à Claude ou GPT-5 via Tchavi, et répond
- une mémoire de conversation par client
- un déploiement sur un VPS avec HTTPS
- et surtout le coût réel, en FCFA, par message et par mois
Le serveur complet tient en 115 lignes, signature et anti-doublon compris. Compte une heure.
Ce qu'il te faut avant de commencer
- Un compte Meta for Developers (gratuit) : developers.facebook.com.
- Un numéro de téléphone qui n'est pas déjà utilisé sur WhatsApp. C'est le piège numéro un. Si le numéro est enregistré sur l'app WhatsApp ou WhatsApp Business, tu dois d'abord le supprimer de l'app avant de pouvoir l'enregistrer sur l'API. Pour tester, Meta te fournit un numéro temporaire, tu peux commencer avec lui.
- Node.js 20.6 ou plus sur ta machine (
--env-filen'existe pas avant). - Une clé API Tchavi. Crée un compte, recharge 3 000 F en mobile money, génère une clé dans le tableau de bord. La clé commence par
sk-tch-. - Un tunnel HTTPS pour le développement. Meta n'envoie les webhooks que vers une URL en HTTPS. En local,
cloudflared tunnel --url http://localhost:3000te donne une URL publique en une commande, gratuitement. ngrok marche aussi.
Étape 1 : créer ton app sur l'API WhatsApp Cloud de Meta
Sur le portail développeur, crée une app de type « Business », puis ajoute le produit WhatsApp. Meta te donne trois choses dans l'onglet « API Setup » :
- un token d'accès temporaire (valable 24 h, on le remplacera par un token permanent à la fin)
- un Phone number ID (l'identifiant du numéro d'envoi, ce n'est pas le numéro lui-même)
- un numéro de test et un champ pour ajouter jusqu'à cinq numéros destinataires autorisés pendant les tests
Ajoute ton propre numéro WhatsApp dans les destinataires de test, valide le code reçu, et envoie-toi le message « hello_world » depuis l'interface. Si tu le reçois, la partie Meta est prête.
Étape 2 : le webhook Node.js qui reçoit les messages
Crée un dossier, initialise le projet, passe-le en modules ES (sans ça les import ne marcheront pas), et installe les deux seules dépendances :
mkdir bot-whatsapp && cd bot-whatsapp
npm init -y
npm pkg set type=module
npm install express openai
Puis un fichier .env (jamais commité, ajoute-le à .gitignore). Le VERIFY_TOKEN, c'est toi qui l'inventes : Meta te le redemandera à l'étape 3 pour vérifier que le webhook est bien le tien.
WHATSAPP_TOKEN=EAAG... # token Meta
META_APP_SECRET=abc123... # App settings > Basic > App secret
PHONE_NUMBER_ID=1234567890 # Phone number ID
VERIFY_TOKEN=un-mot-de-passe-que-tu-choisis
TCHAVI_API_KEY=sk-tch-...
Et voici server.js, en entier :
import crypto from 'node:crypto';
import express from 'express';
import OpenAI from 'openai';
const app = express();
// On garde le corps brut : la signature de Meta porte sur les octets exacts.
app.use(
express.json({
verify: (req, _res, buf) => {
req.rawBody = buf;
},
}),
);
const ai = new OpenAI({
apiKey: process.env.TCHAVI_API_KEY,
baseURL: 'https://tchavi.com/api/v1',
});
const GRAPH = 'https://graph.facebook.com/v26.0';
const SYSTEM_PROMPT = `Tu es l'assistant de la boutique Kèkè Mode à Cotonou.
Tu réponds en français, en deux ou trois phrases maximum, sur un ton chaleureux.
Horaires : lundi à samedi, 9 h à 19 h. Livraison à Cotonou : 1 000 F, sous 24 h.
Paiement : Mobile Money (MTN, Moov) ou espèces à la livraison.
Si tu ne connais pas la réponse, dis que tu transmets à la gérante.`;
// Mémoire de conversation : vingt derniers messages par numéro.
// En production, remplace cette Map par Redis (voir plus bas).
const history = new Map();
function remember(phone, role, content) {
const list = history.get(phone) ?? [];
list.push({ role, content });
let trimmed = list.slice(-20);
// Claude exige que l'historique commence par un message « user ».
while (trimmed.length && trimmed[0].role !== 'user') trimmed = trimmed.slice(1);
history.set(phone, trimmed);
}
async function askModel(phone, userText) {
remember(phone, 'user', userText);
const completion = await ai.chat.completions.create({
model: 'gpt-5-mini',
max_tokens: 800,
reasoning_effort: 'low',
messages: [{ role: 'system', content: SYSTEM_PROMPT }, ...history.get(phone)],
});
const answer = completion.choices[0].message.content?.trim();
if (!answer)
throw new Error(`Réponse vide (finish_reason: ${completion.choices[0].finish_reason})`);
remember(phone, 'assistant', answer);
return answer;
}
async function sendWhatsApp(to, text) {
const res = await fetch(`${GRAPH}/${process.env.PHONE_NUMBER_ID}/messages`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.WHATSAPP_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messaging_product: 'whatsapp',
to,
type: 'text',
text: { body: text },
}),
});
if (!res.ok) console.error('Envoi WhatsApp échoué :', res.status, await res.text());
}
// Meta signe chaque POST avec le secret de ton app. Sans cette vérification,
// n'importe qui peut envoyer de faux messages et brûler tes crédits.
function signatureValide(req) {
const recu = req.get('x-hub-signature-256') ?? '';
const attendu =
'sha256=' +
crypto.createHmac('sha256', process.env.META_APP_SECRET).update(req.rawBody).digest('hex');
return (
recu.length === attendu.length &&
crypto.timingSafeEqual(Buffer.from(recu), Buffer.from(attendu))
);
}
// Meta peut livrer deux fois le même message : on garde les identifiants déjà vus.
const vus = new Set();
// 1. Vérification du webhook (Meta appelle cette route une fois, en GET)
app.get('/webhook', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.VERIFY_TOKEN) {
return res.status(200).send(challenge);
}
return res.sendStatus(403);
});
// 2. Réception des messages (Meta appelle cette route en POST)
app.post('/webhook', async (req, res) => {
if (!signatureValide(req)) return res.sendStatus(401);
// Réponds 200 tout de suite : Meta réessaie si tu mets plus de quelques secondes.
res.sendStatus(200);
const message = req.body?.entry?.[0]?.changes?.[0]?.value?.messages?.[0];
if (!message || message.type !== 'text') return;
if (vus.has(message.id)) return;
vus.add(message.id);
const from = message.from; // numéro du client, format international sans "+"
const text = message.text.body;
try {
const answer = await askModel(from, text);
await sendWhatsApp(from, answer);
} catch (err) {
console.error('Erreur :', err);
await sendWhatsApp(
from,
'Petit souci technique de notre côté, la boutique vous répond dans un instant.',
);
}
});
app.listen(3000, () => console.log('Bot en écoute sur le port 3000'));
Lance-le :
node --env-file=.env server.js
Trois choses méritent qu'on s'arrête dessus dans ce fichier. Ce sont exactement celles qu'on saute, et c'est pour ça qu'on m'écrit ensuite.
On répond 200 avant de traiter le message, et on note les identifiants déjà vus. Meta attend une réponse rapide. Si tu attends la réponse du modèle avant de répondre au webhook, Meta considère que ça a échoué et renvoie le même message plusieurs fois. Ton client reçoit alors trois réponses identiques. Et comme Meta peut aussi relivrer un message sur un simple incident réseau, le Set des identifiants vus est la vraie garantie, pas le 200.
On filtre sur message.type === 'text'. Le webhook reçoit aussi les accusés de lecture, les images, les vocaux, les réactions. Sans ce filtre, le bot plante sur le premier emoji. Le premier.
max_tokens: 800 avec reasoning_effort: 'low', et un system prompt qui impose des réponses courtes. Piège classique sur les modèles GPT-5 : max_tokens plafonne les tokens de raisonnement et la réponse visible ensemble. À 300, le modèle dépense tout à réfléchir et te renvoie un contenu vide. Laisse-lui de la marge, baisse l'effort de raisonnement, et laisse le prompt système faire les réponses courtes. Sur WhatsApp, personne ne lit un roman. Et comme la sortie est la partie chère de la facture, c'est aussi là que tu grattes le plus.
La vérification de signature, elle, n'est pas optionnelle. Ton URL de webhook est publique. Sans le contrôle de X-Hub-Signature-256, n'importe qui peut y poster de faux messages, faire tourner le modèle sur tes crédits et envoyer des WhatsApp à n'importe quel numéro. Le secret se trouve dans les réglages de base de ton app Meta.
Étape 3 : brancher le webhook
Lance ton tunnel :
cloudflared tunnel --url http://localhost:3000
Tu obtiens une URL du type https://xxxx.trycloudflare.com. Dans le portail Meta, onglet Configuration du produit WhatsApp :
- Callback URL :
https://xxxx.trycloudflare.com/webhook - Verify token : la valeur de
VERIFY_TOKENdans ton.env - Clique sur « Verify and save ». Ton terminal doit afficher une requête GET sur
/webhook. - Ensuite, dans « Webhook fields », abonne-toi à
messages. Sans ça, tu ne reçois rien.
Envoie « Bonjour, vous livrez à Calavi ? » depuis ton téléphone au numéro de test. Si tout va bien, le bot répond en deux ou trois secondes.
Si rien ne se passe, dans l'ordre : vérifie que ton numéro est bien dans les destinataires de test, que le champ messages est coché, et que le token d'accès temporaire n'a pas expiré (24 h).
Étape 4 : choisir le modèle, et ce que ça change sur la facture
Le code ci-dessus utilise gpt-5-mini. C'est mon choix par défaut pour un bot de service client en français : rapide, correct, et bon marché. Il se compare comme ça, avec la règle de facturation de Tchavi : chaque appel est facturé au token près, au millième de crédit, sans minimum par requête. Un message WhatsApp typique (prompt système + dix derniers échanges + réponse courte) fait environ 500 tokens en entrée et 150 en sortie, c'est la base des chiffres ci-dessous.
| Modèle | ID à mettre dans le code | Crédits / message | FCFA / message | 3 000 msgs / mois (100 par jour) |
|---|---|---|---|---|
| GPT-5 Nano | gpt-5-nano | 0,21 | 0,07 F | 210 F |
| DeepSeek V4 Flash | deepseek-v4-flash | 0,81 | 0,27 F | 810 F |
| GPT-5 Mini | gpt-5-mini | 1,05 | 0,35 F | 1 050 F |
| Claude Haiku 4.5 | claude-haiku-4-5-20251001 | 3 | 1,00 F | 3 000 F |
| Claude Sonnet 4.6 | claude-sonnet-4-6 | 8,9 | 2,97 F | 8 900 F |
Mon expérience sur ce type de bot : GPT-5 Nano est trop léger dès que le client pose une question un peu tordue (« vous avez le modèle en 42 mais en bleu, livré demain à Porto-Novo ? »). GPT-5 Mini s'en sort bien. Haiku a un français plus naturel, moins « traduit ». Sonnet est meilleur mais tu le sens rarement dans une conversation de vente, et il coûte huit fois plus.
Ce qui marche bien : Mini par défaut, et Sonnet uniquement quand la conversation dépasse cinq échanges (signe que le client a un vrai problème). Ça se fait en une ligne :
// dans askModel, après remember(phone, 'user', userText),
// à la place du `model: 'gpt-5-mini'` écrit en dur
const model = (history.get(phone) ?? []).length > 10 ? 'claude-sonnet-4-6' : 'gpt-5-mini';
C'est pour ça que remember garde vingt messages et pas dix : si le plafond de l'historique et le seuil de bascule sont le même nombre, la bascule ne se déclenche jamais. Et c'est aussi pour ça que remember coupe l'historique jusqu'au premier message « user » : Claude refuse une conversation qui commence par une réponse de l'assistant, alors que GPT s'en accommode. Sans cette ligne, la première bascule vers Sonnet renvoie une erreur 400.
Pour un comparatif complet des prix en FCFA, y compris images, audio et vidéo, j'ai publié un tableau détaillé.
Étape 5 : passer en production
Token permanent
Le token d'accès temporaire expire au bout de 24 h. Pour un token qui ne meurt pas, va dans Business Settings de ton Meta Business, section System Users, crée un utilisateur système de type Admin, attribue-lui ton app et ton compte WhatsApp Business, puis génère un token avec les permissions whatsapp_business_messaging et whatsapp_business_management. C'est ce token que tu mets dans WHATSAPP_TOKEN en production.
Ton vrai numéro
Ajoute ton numéro de téléphone dans le portail (onglet « Phone numbers »), valide-le par SMS ou appel. Rappel : il ne doit pas être actif sur l'app WhatsApp. Tant que ton entreprise n'est pas vérifiée par Meta, tu es limité à 250 conversations initiées par le bot par 24 h, une limite qui s'applique à l'ensemble de ton portefeuille Meta Business, pas par numéro. Mais les réponses aux messages entrants ne sont pas limitées, ce qui est le cas d'usage ici.
Mémoire : remplacer la Map par Redis
La Map en mémoire perd tout au redémarrage et ne marche pas si tu lances deux instances. Sur un VPS, Redis fait l'affaire (npm install redis, version 5 ou plus) :
import { createClient } from 'redis';
const redis = await createClient({ url: process.env.REDIS_URL }).connect();
async function remember(phone, role, content) {
const key = `chat:${phone}`;
await redis.rPush(key, JSON.stringify({ role, content }));
await redis.lTrim(key, -20, -1);
await redis.expire(key, 60 * 60 * 24); // on oublie après 24 h de silence
}
async function getHistory(phone) {
const raw = await redis.lRange(`chat:${phone}`, 0, -1);
const list = raw.map((s) => JSON.parse(s));
while (list.length && list[0].role !== 'user') list.shift();
return list;
}
L'expiration à 24 h correspond à la fenêtre de service WhatsApp : au-delà, une nouvelle conversation commence de toute façon.
Deux reports à faire dans askModel, sinon rien ne marche : remember est devenu asynchrone, et l'historique ne se lit plus dans la Map.
async function askModel(phone, userText) {
await remember(phone, 'user', userText);
const completion = await ai.chat.completions.create({
model: 'gpt-5-mini',
max_tokens: 800,
reasoning_effort: 'low',
messages: [{ role: 'system', content: SYSTEM_PROMPT }, ...(await getHistory(phone))],
});
const answer = completion.choices[0].message.content?.trim();
if (!answer)
throw new Error(`Réponse vide (finish_reason: ${completion.choices[0].finish_reason})`);
await remember(phone, 'assistant', answer);
return answer;
}
Le Set des identifiants vus suit le même chemin, avec un redis.set en mode NX (n'écrire que si la clé n'existe pas) et une expiration d'une heure :
const nouveau = await redis.set(`vu:${message.id}`, '1', { NX: true, EX: 3600 });
if (!nouveau) return; // déjà traité
Si tu as mis en place la bascule de modèle de l'étape 4, remplace aussi history.get(phone) par await getHistory(phone) dans la ligne du ternaire.
Déploiement sur un VPS
Un VPS d'entrée de gamme (5 $, soit environ 3 000 F par mois) chez Hetzner, Contabo ou OVH suffit largement pour plusieurs milliers de messages par jour. Le minimum :
# Sur le serveur (pas de dépôt git ? un scp du dossier fait aussi l'affaire)
git clone <ton-repo> && cd bot-whatsapp && npm ci
nano .env # le .env n'est pas dans le dépôt : recopie-le ici à la main
npm install -g pm2
pm2 start server.js --name bot-whatsapp --cwd /home/deploy/bot-whatsapp --node-args="--env-file=/home/deploy/bot-whatsapp/.env"
pm2 save && pm2 startup
Et un reverse proxy avec HTTPS devant. Caddy le fait en trois lignes, avec le certificat renouvelé automatiquement :
bot.tondomaine.com {
reverse_proxy localhost:3000
}
Mets ensuite https://bot.tondomaine.com/webhook comme Callback URL chez Meta, à la place de l'URL du tunnel.
Passer les vocaux
Beaucoup de clients envoient des messages vocaux. Tu peux les gérer avec Whisper : télécharge le média en deux temps. GET https://graph.facebook.com/v26.0/{media_id} (avec ton token) renvoie un champ url, puis tu fais un second GET sur cette URL en renvoyant le même en-tête Authorization: Bearer, parce qu'elle n'est pas publique et expire au bout de 5 minutes. Ensuite audio.transcriptions.create avec model: 'whisper-1', et tu traites le texte comme un message normal. Un vocal de 30 secondes coûte 13 crédits, soit 4,3 F : Whisper se facture à la minute entamée, donc 30 secondes et 60 secondes coûtent pareil. C'est une vingtaine de lignes. Je ne les mets pas ici pour ne pas alourdir le fichier, mais si tu bloques, écris-moi.
Combien coûte un chatbot WhatsApp IA, tout compris
Prenons une boutique qui reçoit 100 messages par jour, avec un bot sur GPT-5 Mini.
| Poste | Coût mensuel |
|---|---|
| API IA (Tchavi, GPT-5 Mini) | 1 050 F |
| Messages WhatsApp (réponses client) | facturés par Meta depuis le 1er octobre 2026, voir ci-dessous |
| VPS | 3 000 à 4 000 F |
| Nom de domaine | environ 1 000 F / mois lissé |
| Total hors WhatsApp | 5 050 à 6 050 F / mois |
La ligne WhatsApp mérite qu'on s'y arrête, parce qu'elle vient de changer. Jusqu'au 30 septembre 2026, les réponses à un message entrant dans la fenêtre de 24 h (ce que Meta appelle les service messages) étaient gratuites, et seuls les messages que le bot initie (relances, promotions, notifications) étaient facturés à l'unité. Depuis le 1er octobre 2026, Meta facture aussi les réponses, au tarif des messages utilitaires du pays du destinataire, sans palier dégressif. Le Bénin, le Sénégal et la Côte d'Ivoire sont tous dans la zone « Rest of Africa » de Meta.
À 100 messages par jour, ça fait environ 3 000 réponses facturées par mois, un poste du même ordre de grandeur que la facture IA. Je ne mets pas de chiffre en francs ici parce que le tarif par pays bouge et que Meta l'a déjà révisé plusieurs fois depuis 2025 : regarde la grille officielle pour ton pays avant de chiffrer quoi que ce soit à un client, et enregistre un moyen de paiement sur ton compte Meta Business, sinon les envois s'arrêtent. Une exception qui reste gratuite : les conversations qui démarrent depuis une pub « Click to WhatsApp » ou un bouton de page Facebook ouvrent une fenêtre de 72 h sans frais.
Attention au calcul que presque tout le monde fait de travers : l'infrastructure se partage, l'API non. Une boutique à 100 messages par jour te coûte 1 050 F d'API à elle seule, chaque mois, quel que soit le nombre de boutiques. J'avais écrit 2 000 F par boutique dans l'article de juin ; avec la ligne WhatsApp de Meta qui arrive en plus, c'est trop juste. Le forfait qui tient debout, c'est 5 000 F plafonnés à 150 messages par jour, et le dépassement facturé à part. Sur 30 boutiques à ce forfait, à 100 messages par jour chacune : 150 000 F encaissés, 31 500 F d'API, et 5 000 F de VPS et de domaine partagés entre toutes (une seule app, un SYSTEM_PROMPT par numéro). Il te reste autour de 113 000 F, moins la facture WhatsApp de Meta, qui elle aussi se cumule par message. C'est elle qui justifie le plafond dans le forfait, plus que l'IA.
Ce qui casse, et pourquoi
Répondre au webhook après le traitement. Je l'ai dit plus haut, je le redis ici : c'est le message que je reçois le plus souvent, toujours formulé pareil, « mon bot répond trois fois ».
Oublier le filtre sur les messages du bot lui-même. Selon la configuration, tu peux recevoir des événements de statut (statuses) pour tes propres envois. Le filtre sur messages[0] et type === 'text' les ignore.
Renvoyer tout l'historique. Cinquante messages d'historique, c'est 5 000 tokens et plus en entrée à chaque appel, cinq tranches facturées au lieu d'une. Vingt messages suffisent, l'expiration Redis fait le reste.
Laisser le modèle inventer. Sans la phrase « si tu ne connais pas la réponse, dis que tu transmets à la gérante », le bot invente un prix ou un délai de livraison. Sur WhatsApp, ça finit en capture d'écran envoyée à la boutique.
Ne pas logger les coûts. Chaque réponse Tchavi porte un en-tête X-Credits-Used. Logge-le avec le numéro (haché) et tu sais exactement quel client te coûte combien. Avec le SDK OpenAI, completion n'expose pas les en-têtes, utilise ai.chat.completions.create(...).withResponse() pour les récupérer.
Pour aller plus loin
Le bot ci-dessus répond. Les versions suivantes, dans l'ordre où mes clients les demandent :
- Passage à un humain : un mot-clé (« patron », « quelqu'un ») ou une détection par le modèle qui coupe le bot et prévient le gérant.
- Boutons et listes WhatsApp :
type: 'interactive'à la place detype: 'text'pour proposer des choix cliquables. Meilleure expérience, moins de tokens. - Catalogue produit : le modèle appelle une fonction (
tools) qui interroge ta base de stock au lieu de deviner. - Multi-boutiques : une table
numéro → prompt système + clé APIet tu vends le même serveur à dix commerçants.
Le code de cet article est volontairement minimal pour rester lisible. Si tu veux la version avec Redis, les vocaux et les boutons, dis-le-moi, je la publierai en réponse.
FAQ
Est-ce que c'est autorisé par WhatsApp ? Oui, tant que tu passes par l'API officielle (Cloud API) et que tu respectes la politique commerciale de Meta : pas de messages non sollicités, opt-in pour les campagnes. Les solutions non officielles basées sur l'automatisation de WhatsApp Web font bannir le numéro.
Faut-il passer par Twilio ou 360dialog ? Non, pas pour un bot WhatsApp seul. L'API Cloud de Meta est directe. Elle était gratuite pour les réponses jusqu'au 30 septembre 2026 et se facture par message depuis (voir la section coûts), mais Twilio ou 360dialog ajoutent leur propre couche et leur propre facturation par-dessus. Ils deviennent utiles si tu veux gérer SMS et WhatsApp au même endroit.
Ça marche depuis le Sénégal, la Côte d'Ivoire ou le Togo ?
Rien dans ce tutoriel n'est spécifique au Bénin. Côté Tchavi tu recharges en mobile money selon ton pays (Orange Money, MTN MoMo, et les autres opérateurs listés sur /api/payments/providers). La seule chose à réécrire, c'est le SYSTEM_PROMPT : les moyens de paiement et les zones de livraison d'une boutique de Dakar ne sont pas ceux de Cotonou.
Le bot peut-il parler wolof, fon, lingala ? GPT-5 Mini et Claude comprennent ces langues à des degrés variables et répondent souvent en mélangeant. Pour un usage sérieux en langue locale, teste sur une vingtaine de messages réels avant de promettre quoi que ce soit au client.
Que se passe-t-il si mes crédits Tchavi tombent à zéro ?
L'appel API renvoie une erreur 402 et le catch du code envoie le message de secours au client. Active l'alerte par email dans ton tableau de bord Tchavi pour être prévenu avant.
Je peux utiliser Python à la place de Node.js ?
Oui, la logique est identique : Flask ou FastAPI pour le webhook, le SDK openai avec base_url="https://tchavi.com/api/v1", et les mêmes appels HTTP vers Graph API.
Cet article fait partie de la série « Construire un SaaS IA depuis l'Afrique francophone ». Le précédent : les prix des APIs IA en FCFA. Voir aussi : payer OpenAI, Claude et Mistral sans carte Visa. Pour suivre les prochains, Tchavi est sur LinkedIn.