Documentation
Connexion Directe WhatsApp
Comment connecter un numéro par QR, envoyer des messages, recevoir les événements dans votre système et éviter les restrictions de Meta.
Mis à jour le 12 novembre 2026
Sommaire
Vue d'ensemble
La Connexion Directe relie un numéro WhatsApp ordinaire (personnel ou WhatsApp Business) à notre API en scannant un code QR, comme pour WhatsApp Web. Vous envoyez ensuite des messages et recevez ce que vos clients écrivent, depuis n8n, votre CRM ou votre propre code.
Ce que vous recevez :
une clé d'accès pour envoyer des messages et consulter l'état de vos numéros ;
un webhook : chaque message reçu et chaque changement d'état arrivent à l'adresse de votre choix, signés ;
l'espace client, où vous connectez le numéro et suivez votre consommation.
Toutes les requêtes vont vers :
https://wa.automate-ai.digitalConnaissez le risque avant de commencer. La Connexion Directe n'est pas le canal officiel de Meta. Meta peut restreindre un numéro qu'elle considère comme automatisé, et cela échappe à notre contrôle. Suivez les bonnes pratiques et, pour de gros volumes ou des campagnes, utilisez l'API Officielle.
Démarrer
1. Connecter le numéro
Dans l'espace client, sous Mes produits, cliquez sur Connecter ce numéro. Un code QR apparaît. Sur le téléphone, ouvrez WhatsApp, allez dans Appareils connectés → Connecter un appareil et scannez le code.
Le code reste actif 5 minutes. S'il expire, cliquez sur Reconnecter. Le numéro apparaît comme connecté dès la fin du scan.
Le téléphone n'a pas besoin de rester en ligne : la connexion fonctionne comme un appareil de plus sur votre compte.
2. Configurer le webhook et la clé
Dans l'espace client, sous Intégration technique :
Webhook : indiquez l'adresse qui recevra les événements. L'espace client affiche une seule fois le secret du webhook, qui sert à vérifier la signature. Conservez-le.
Clé d'accès : générez la clé. Elle n'apparaît, elle aussi, qu'une seule fois.
Authentification
Chaque requête porte la clé dans l'en-tête x-api-key :
curl https://wa.automate-ai.digital/instances/ID_DU_NUMERO \
-H "x-api-key: wak_xxxxxxxx_xxxxxxxx"Toutes les réponses ont la même forme :
{ "ok": true, "data": { "...": "..." } }{ "ok": false, "error": { "code": "INSTANCE_NOT_FOUND", "message": "…", "retryable": false } }L'ID_DU_NUMERO (instanceId) figure dans chaque événement reçu par votre webhook.
Une nouvelle clé invalide la précédente dès sa génération. Tout ce qui utilisait l'ancienne cesse de fonctionner jusqu'à sa mise à jour.
Envoyer des messages
curl -X POST https://wa.automate-ai.digital/instances/ID_DU_NUMERO/messages \
-H "x-api-key: $CLE" -H "content-type: application/json" \
-d '{
"to": { "kind": "contact", "id": "33612345678" },
"content": { "type": "text", "body": "Bonjour ! Votre commande est en route." },
"idempotencyKey": "commande-1234-confirmation"
}'to.idest le numéro au format international, sans+ni espaces. Pour un groupe :{ "kind": "group", "id": "ID_DU_GROUPE" }.idempotencyKey(facultatif, recommandé) : si la même requête est répétée avec la même clé, le message ne part pas deux fois.replyTo(facultatif) : leproviderMessageIddu message auquel vous répondez, pour l'afficher en citation.
La réponse est 202 avec l'identifiant du message :
{ "ok": true, "data": { "messageId": "44444444-…", "queuedAt": "2026-11-12T10:00:00.000Z" } }202 signifie que le message est entré dans la file, pas qu'il est parti. Le résultat arrive ensuite sur le webhook, sous forme de message.status.
Les messages partent dans l'ordre d'arrivée, avec un petit intervalle aléatoire entre eux. C'est voulu : les envois en rafale sont ce qui pousse Meta à restreindre des numéros.
Types de contenu
| Champs |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
media vaut { "kind": "url", "url": "https://…" }, ou { "kind": "base64", "data": "…", "mimeType": "image/png" }.
Les boutons n'existent pas sur ce canal. Une requête avec "type": "buttons" échoue avec CAPABILITY_UNAVAILABLE. L'API ne les transforme pas en liste numérotée : la réponse du client arriverait en texte libre, et un flux qui attend le clic attendrait pour toujours. Pour les boutons, utilisez l'API Officielle.
Les modèles non plus. Avec la Connexion Directe, vous pouvez écrire à n'importe qui à tout moment, sans modèles ni fenêtre de 24 heures. Utilisez toujours /messages.
Groupes
Le numéro peut envoyer et recevoir des messages dans les groupes dont il fait partie. Pour que votre flux ne soit pas submergé par toutes les conversations de groupe, seuls les messages des groupes que vous autorisez arrivent sur le webhook.
Listez les groupes du numéro :
curl https://wa.automate-ai.digital/instances/ID_DU_NUMERO/groups -H "x-api-key: $CLE"{ "ok": true, "data": [{ "id": "120363012345678901", "name": "Équipe commerciale" }] }Autorisez ceux que vous voulez recevoir. La liste remplace la précédente et s'applique immédiatement :
curl -X PATCH https://wa.automate-ai.digital/instances/ID_DU_NUMERO \
-H "x-api-key: $CLE" -H "content-type: application/json" \
-d '{ "allowedGroups": ["120363012345678901"] }'Les messages de groupe arrivent avec via, le groupe, et from, l'auteur.
Événements
Chaque événement arrive sur votre webhook en POST, avec un corps JSON. Ce canal a trois types :
| Quand |
|---|---|
| quelqu'un a écrit |
| un envoi a changé d'état : |
| l'état du numéro a changé |
Ignorez les types que vous ne connaissez pas, en répondant 2xx. La liste va s'allonger, et un flux qui suppose « si ce n'est pas A, c'est B » traite un nouveau type comme ce qu'il n'est pas.
message.received
{
"type": "message.received",
"eventId": "0f0f6ab7-2a2f-4f0b-9b6a-9a5f7d5d31c2",
"instanceId": "0d9b1b6f-6a2a-4a4c-9f66-3f6a3f0b6a2a",
"provider": "baileys",
"occurredAt": "2026-11-12T18:41:07.000Z",
"providerMessageId": "3EB0C767D26B8A1F",
"from": "33612345678",
"pushName": "Marie",
"content": { "type": "text", "body": "Bonjour, où en est ma commande ?" },
"windowExpiresAt": null
}Types de content :
| Champs |
|---|---|
|
|
|
|
|
|
|
|
|
|
Le storageUrl du média est temporaire : il sert à télécharger le fichier tout de suite, pas à le conserver.
Si le regroupement des messages est activé sur votre numéro, les textes consécutifs d'un même client arrivent en un seul événement, séparés par des retours à la ligne. Cet événement contient tous les identifiants dans providerMessageIds.
message.status
Sur ce canal arrivent sent (le message est parti) et failed (il n'est pas parti : failureCode dit pourquoi). Chacun contient le messageId que l'API vous a renvoyé à l'envoi. Les états « livré » et « lu » ne sont pas envoyés sur ce canal.
connection.updated
Contient status : connecting, qr_required, connected, disconnected ou logged_out.
disconnected: la connexion est tombée. L'API se reconnecte seule.logged_outavecrequiresReauth: true: le numéro doit être reconnecté dans l'espace client, avec un nouveau QR. Cela arrive, par exemple, quand quelqu'un ferme la session dans Appareils connectés sur le téléphone.
Vérifier la signature
Chaque requête du webhook contient :
x-timestamp: 1794519022
x-signature: sha256=9c1f…La signature est un HMAC-SHA256, calculé avec le secret du webhook, sur x-timestamp + "." + corps brut. Pour la vérifier :
refusez la requête s'il manque
x-signatureoux-timestamp;refusez-la si
x-timestamps'écarte de plus de 5 minutes de votre horloge ;calculez le HMAC sur le corps brut, octet par octet. Si vous resérialisez le JSON, la signature échoue pour certains événements ;
comparez avec une comparaison à temps constant.
const crypto = require("crypto");
function signatureValide(secret, timestamp, corpsBrut, signature, signatureSuivante) {
if (!signature || !timestamp) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const attendu = Buffer.from(
"sha256=" + crypto.createHmac("sha256", secret).update(`${timestamp}.${corpsBrut}`).digest("hex")
);
const correspond = (h) => !!h && Buffer.from(h).length === attendu.length && crypto.timingSafeEqual(Buffer.from(h), attendu);
return correspond(signature) || correspond(signatureSuivante);
}Vérifiez avant de répondre. Si la signature échoue, répondez 401 : l'événement est conservé de notre côté et n'est pas perdu. Si vous répondez 2xx avant de vérifier, un événement refusé ensuite est perdu sans trace.
Changer le secret sans perdre d'événements. Pendant un changement, chaque requête contient aussi x-signature-next, signé avec le nouveau secret. Acceptez l'une ou l'autre signature. Vous pouvez ainsi passer au nouveau secret quand vous voulez, sans période où les événements échouent.
Erreurs
Dans la réponse à la requête. Elles se corrigent dans la requête elle-même :
HTTP |
| Quand |
|---|---|---|
400 |
| le corps n'est pas correct (le message dit pourquoi) |
401 |
| clé absente, erronée ou révoquée |
404 |
| le numéro n'existe pas dans votre compte |
409 |
| uniquement sur |
Sur le webhook, dans un message.status à failed. Ce qui dépend de WhatsApp arrive ici, après le 202 :
| Quand |
|---|---|
| le numéro n'est pas connecté. La file réessaie avant d'abandonner |
| quelque chose que ce canal ne fait pas (par exemple, les boutons) |
| WhatsApp a refusé ou n'a pas confirmé l'envoi. La raison figure dans |
Livraison des événements
Répondez 2xx dès réception, avant de traiter l'événement. Chaque tentative dispose de 10 secondes. Un endpoint qui ne répond qu'à la fin du flux risque de recevoir le même événement une deuxième fois.
Dédupliquez par
eventId. Chaque événement est livré au moins une fois et peut arriver en double.Triez par
occurredAt, pas par ordre d'arrivée.Nouvelles tentatives : sur un 5xx, un 429 ou sans réponse, nous réessayons jusqu'à 8 fois sur environ 30 minutes. Un 4xx (sauf 429) n'est pas réessayé : l'événement est conservé de notre côté.
Bonnes pratiques
Ce que Meta considère comme une automatisation abusive, et qui la pousse à restreindre des numéros :
des messages à des personnes qui ne les ont pas demandés, surtout en nombre et avec le même texte ;
beaucoup de messages d'affilée vers des contacts qui n'ont jamais écrit au numéro ;
des numéros neufs qui envoient tout de suite en volume.
Ce qui aide :
répondez à ceux qui vous écrivent, et envoyez des avis aux clients qui vous ont déjà parlé ;
variez le texte et évitez les liens raccourcis ;
démarrez doucement avec un nouveau numéro ;
pour les campagnes, utilisez l'API Officielle avec des modèles approuvés.
Consommation et limites
Chaque numéro inclut 3 000 messages envoyés par cycle de facturation. Les messages reçus ne comptent pas.
Au-delà, vous achetez des blocs de 1 000 messages dans l'espace client.
La consommation de chaque numéro figure dans l'espace client, sous Mes produits.
Une remarque sur les expéditeurs. WhatsApp commence à identifier certaines conversations par un code interne au lieu du numéro de téléphone. Quand le téléphone de l'expéditeur n'est pas connu, le message est enregistré mais n'est pas envoyé au webhook : le champ from doit être un numéro auquel on peut répondre, et ce code n'en est pas un.
