Trois panneaux de code côte à côte montrant le même code d'envoi de message WhatsApp Cloud API en Node.js, Python et PHP sur fond sombre de terminal
Sections : Fondamentaux de l'API · Message texte · Modèle avec variables · Image et document · Boutons interactifs · Localisation · Envoi en masse avec rate limiting · Gestion d'erreurs et reprise · Wrappers de classe complets (Node / Python / PHP)

Fondamentaux de l'API : l'unique endpoint par lequel tout passe

Chaque message WhatsApp sortant — quel que soit le type — passe par un seul endpoint HTTP POST. Le type de message et le contenu sont dans le body JSON. C'est tout. Pas de WebSockets, pas de streaming, pas de long-polling.

Trois valeurs dont vous avez besoin avant d'exécuter du code :

  • Phone Number ID — un ID numérique pour votre numéro WhatsApp Business (ressemble à 123456789012345). Différent de votre numéro de téléphone réel.
  • Access Token — un token permanent System User avec la permission whatsapp_business_messaging. N'utilisez jamais de token utilisateur temporaire en production.
  • Numéro de téléphone du destinataire — au format E.164 : code pays + numéro, sans espaces, sans tirets, sans le signe + dans la valeur JSON (par ex. 15550001234 pour un numéro américain, ou avec + également accepté : +15550001234).

Un envoi réussi renvoie HTTP 200 avec un body comme :

Types de message : session vs modèle

Avant de choisir un type de message, vous devez savoir si vous êtes dans une fenêtre de service. La fenêtre de session (24 h après qu'un client vous a écrit, ou 72 h après un clic sur publicité Click-to-WhatsApp) détermine quels types de messages sont gratuits et sans restriction.

text
Texte brut. Formatage WhatsApp de type markdown supporté. Jusqu'à 4 096 caractères.
Gratuit dans la fenêtre
template
Format pré-approuvé avec emplacements de variables. Requis pour initier un contact hors fenêtre.
Approbation requise
image
JPEG ou PNG. Max 5 Mo. URL ou media ID. Légende optionnelle.
Gratuit dans la fenêtre
document
PDF, DOCX, XLSX. Max 100 Mo. URL ou media ID. Nom de fichier affiché.
Gratuit dans la fenêtre
video
MP4 ou 3GPP. Max 16 Mo. URL ou media ID. Légende optionnelle.
Gratuit dans la fenêtre
interactive
Réponses par boutons (max 3) ou messages liste (max 10 éléments). Le client tape pour répondre.
Gratuit dans la fenêtre
location
Envoyez lat/lng avec nom et adresse optionnels. S'affiche sur une carte dans WhatsApp.
Gratuit dans la fenêtre
audio
MP3 ou AAC. Max 16 Mo. URL ou media ID. Légende non supportée.
Gratuit dans la fenêtre

Envoyer un message texte

L'envoi le plus simple. Fonctionne dans la fenêtre de service de 24 heures sans frais Meta (dans le quota mensuel de 1 000 conversations de service gratuites). Hors fenêtre, utilisez un modèle à la place.

Node.js (native fetch)
sendText.js
async function sendText(to, message) { const url = `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`; const res = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', recipient_type: 'individual', to, type: 'text', text: { body: message, preview_url: false }, }), }); if (!res.ok) throw new Error(`WhatsApp error: ${await res.text()}`); return (await res.json()).messages[0].id; // wamid — conservez-le pour le suivi de statut } // Utilisation const messageId = await sendText('+15550001234', 'Hello from WhatsApp Cloud API! 👋');
Python (requests)
send_text.py
import os, requests def send_text(to: str, message: str) -> str: url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "recipient_type": "individual", "to": to, "type": "text", "text": { "body": message, "preview_url": False }, } ) res.raise_for_status() return res.json()["messages"][0]["id"] # Utilisation message_id = send_text("+15550001234", "Hello from WhatsApp Cloud API! 👋")
PHP (cURL)
send_text.php
function sendText(string $to, string $message): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $body = [ 'messaging_product' => 'whatsapp', 'recipient_type' => 'individual', 'to' => $to, 'type' => 'text', 'text' => ['body' => $message, 'preview_url' => false], ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $response = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) { throw new RuntimeException('WhatsApp error: ' . json_encode($response)); } return $response['messages'][0]['id']; // conservez-le pour le suivi de statut } // Utilisation $messageId = sendText('+15550001234', 'Hello from WhatsApp Cloud API! 👋');

Envoyer un message modèle avec variables

Les modèles sont requis pour le premier message d'une nouvelle conversation, ou tout message envoyé en dehors de la fenêtre de service de 24 heures. Les modèles sont définis dans WhatsApp Manager, approuvés par Meta, et peuvent inclure des emplacements de variables {{1}} {{2}}. Votre appel API remplit ces variables au moment de l'envoi.

Délai d'approbation des modèles : Les modèles utility simples (confirmations de commande, mises à jour d'expédition) sont généralement approuvés en 1–24 heures. Les modèles marketing prennent 1–5 jours ouvrés. Vous pouvez vérifier le statut d'approbation via l'abonnement webhook message_template_status_update ou dans WhatsApp Manager. Les modèles au statut rejeté ne peuvent pas être envoyés — envoyer un modèle non-APPROVED renvoie une erreur 400.
Node.js
sendTemplate.js
async function sendTemplate(to, templateName, languageCode, variables = []) { const url = `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`; const body = { messaging_product: 'whatsapp', to, type: 'template', template: { name: templateName, language: { code: languageCode }, // par ex. 'en', 'pt_BR', 'de' components: variables.length ? [{ type: 'body', parameters: variables.map(v => ({ type: 'text', text: v })), }] : [], }, }; const res = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); if (!res.ok) throw new Error(`Template send failed: ${await res.text()}`); return (await res.json()).messages[0].id; } // Exemple : modèle "order_confirmation" avec texte du body {{1}}="order #1234" {{2}}="2h" await sendTemplate( '+15550001234', 'order_confirmation', 'en', ['order #1234', '2 hours'] );
Python
send_template.py
def send_template(to: str, template_name: str, language_code: str, variables: list = []) -> str: url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" components = [] if variables: components.append({ "type": "body", "parameters": [{ "type": "text", "text": v } for v in variables], }) res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "to": to, "type": "template", "template": { "name": template_name, "language": { "code": language_code }, "components": components, }, } ) res.raise_for_status() return res.json()["messages"][0]["id"] # Utilisation send_template("+15550001234", "order_confirmation", "en", ["order #1234", "2 hours"])
PHP
send_template.php
function sendTemplate(string $to, string $templateName, string $langCode, array $variables = []): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $components = []; if (!empty($variables)) { $components[] = [ 'type' => 'body', 'parameters' => array_map( fn($v) => ['type' => 'text', 'text' => $v], $variables ), ]; } $body = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => 'template', 'template' => [ 'name' => $templateName, 'language' => ['code' => $langCode], 'components' => $components, ], ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) throw new RuntimeException(json_encode($res)); return $res['messages'][0]['id']; }

Envoyer une image ou un document

Les messages média référencent un fichier soit par URL (le fichier doit être accessible publiquement via HTTPS) soit par Media ID (obtenu après avoir téléversé le fichier vers l'endpoint média de Meta). L'URL est plus simple pour la plupart des cas — la Cloud API la récupère et la met en cache. Les Media IDs sont meilleurs pour les actifs fréquemment réutilisés comme des images produit ou des PDF de marque.

Node.js
sendMedia.js
// Envoyer une image par URL publique async function sendImage(to, imageUrl, caption = '') { return sendMedia(to, 'image', { link: imageUrl, caption }); } // Envoyer un document par URL publique async function sendDocument(to, docUrl, filename, caption = '') { return sendMedia(to, 'document', { link: docUrl, filename, caption }); } // Sender média générique — fonctionne pour image, vidéo, audio, document async function sendMedia(to, type, mediaObj) { const res = await fetch( `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', to, type, [type]: mediaObj, // { link, caption } OU { id, caption } }), } ); if (!res.ok) throw new Error(`Media send failed: ${await res.text()}`); return (await res.json()).messages[0].id; } // Utilisation await sendImage('+15550001234', 'https://yourdomain.com/promo.jpg', 'Check out our new product! 🚀'); await sendDocument('+15550001234', 'https://yourdomain.com/invoice.pdf', 'invoice-2026.pdf');
Python
send_media.py
def send_media(to: str, media_type: str, media_obj: dict) -> str: url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "to": to, "type": media_type, media_type: media_obj, # par ex. {"link": url, "caption": "..."} } ) res.raise_for_status() return res.json()["messages"][0]["id"] def send_image(to: str, url: str, caption: str = "") -> str: return send_media(to, "image", { "link": url, "caption": caption }) def send_document(to: str, url: str, filename: str, caption: str = "") -> str: return send_media(to, "document", { "link": url, "filename": filename, "caption": caption })
PHP
send_media.php
function sendMedia(string $to, string $type, array $mediaObj): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $body = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => $type, $type => $mediaObj, // ['link' => url, 'caption' => '...'] ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) throw new RuntimeException(json_encode($res)); return $res['messages'][0]['id']; } // Utilisation sendMedia('+15550001234', 'image', ['link' => 'https://yourdomain.com/img.jpg', 'caption' => 'New product!']); sendMedia('+15550001234', 'document', ['link' => 'https://yourdomain.com/invoice.pdf', 'filename' => 'invoice.pdf']);

Envoyer un message interactif avec boutons

Les messages interactifs à boutons permettent aux clients de répondre par un tap au lieu de taper. Maximum 3 boutons par message. Les titres de boutons sont limités à 20 caractères. Lorsqu'un client tape sur un bouton, votre webhook reçoit un message de type interactive avec l'id du bouton dans msg.interactive.button_reply.id.

Node.js
sendButtons.js
async function sendButtons(to, bodyText, buttons) { // buttons: [{ id: 'btn_yes', label: 'Yes' }, { id: 'btn_no', label: 'No' }] const res = await fetch( `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', to, type: 'interactive', interactive: { type: 'button', body: { text: bodyText }, action: { buttons: buttons.map(b => ({ type: 'reply', reply: { id: b.id, title: b.label.slice(0, 20) }, // limite 20 caractères })), }, }, }), } ); if (!res.ok) throw new Error(`Button send failed: ${await res.text()}`); return (await res.json()).messages[0].id; } // Utilisation await sendButtons( '+15550001234', 'Would you like a callback from our team?', [ { id: 'yes_callback', label: 'Yes, call me!' }, { id: 'no_thanks', label: 'No thanks' }, ] );
Python
send_buttons.py
def send_buttons(to: str, body_text: str, buttons: list) -> str: # buttons: [{"id": "btn_yes", "label": "Yes"}, ...] max 3 boutons url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "to": to, "type": "interactive", "interactive": { "type": "button", "body": { "text": body_text }, "action": { "buttons": [ { "type": "reply", "reply": { "id": b["id"], "title": b["label"][:20] }} for b in buttons ] }, }, } ) res.raise_for_status() return res.json()["messages"][0]["id"]
PHP
send_buttons.php
function sendButtons(string $to, string $bodyText, array $buttons): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $btns = array_map(fn($b) => [ 'type' => 'reply', 'reply' => ['id' => $b['id'], 'title' => mb_substr($b['label'], 0, 20)], ], $buttons); $body = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => 'interactive', 'interactive' => [ 'type' => 'button', 'body' => ['text' => $bodyText], 'action' => ['buttons' => $btns], ], ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) throw new RuntimeException(json_encode($res)); return $res['messages'][0]['id']; }

Envoyer une localisation

Les messages de localisation s'affichent comme une épingle sur une carte dans WhatsApp avec un nom et une adresse optionnels en dessous. Les clients peuvent taper pour ouvrir dans leur app de cartes. Pas besoin d'URL ni de media ID — juste des coordonnées.

Envoi en masse : rate limits et le bon pattern

La WhatsApp Cloud API permet 80 messages par seconde par numéro de téléphone par défaut. À ce rythme, envoyer 10 000 messages prend environ 2 minutes. Sans rate limiting, une boucle naïve épuise la limite presque immédiatement et vous commencez à recevoir des erreurs 429 en pleine campagne. Le bon pattern : ajoutez un délai fixe entre les envois et implémentez un backoff exponentiel sur les 429.

Node.js — Sender en masse avec rate limiting
bulkSend.js
const DELAY_MS = 20; // 20 ms entre envois = 50 msg/s — sûr sous la limite de 80 msg/s const MAX_RETRIES = 4; async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } async function sendWithRetry(to, message, attempt = 0) { try { return await sendText(to, message); } catch (err) { const is429 = err.message.includes('429'); if (!is429 || attempt >= MAX_RETRIES) throw err; const waitMs = Math.min(1000 * 2 ** attempt + Math.random() * 500, 60000); console.warn(`Rate limited — waiting ${Math.round(waitMs)}ms (attempt ${attempt + 1})`); await sleep(waitMs); return sendWithRetry(to, message, attempt + 1); } } async function bulkSend(contacts, messageBody) { const results = { sent: 0, failed: [] }; for (const { phone, name } of contacts) { try { const personalised = messageBody.replace('{{name}}', name); await sendWithRetry(phone, personalised); results.sent++; } catch (err) { results.failed.push({ phone, error: err.message }); } await sleep(DELAY_MS); // espacement pour rate limit } console.log(`Sent: ${results.sent}, Failed: ${results.failed.length}`); return results; } // Utilisation await bulkSend( [{ phone: '+15550001234', name: 'Alice' }, { phone: '+15550005678', name: 'Bob' }], 'Hi {{name}}, your order is ready! 🎉' );

Gestion des erreurs : ce que signifie chaque status code

HTTP StatusCause couranteCorrectif
200 OKMessage accepté par Meta — pas encore livréStockez le wamid renvoyé et écoutez les webhooks de statut de livraison
400 Bad RequestJSON invalide, champ obligatoire manquant, modèle non approuvé, nombre de variables de modèle ne correspond pasVérifiez le body de la réponse d'erreur — Meta renvoie un error.message détaillé expliquant le champ ou la contrainte exacte qui a échoué
401 UnauthorizedAccess token invalide ou expiré ; header Authorization manquantRégénérez un token permanent System User. N'utilisez jamais de token utilisateur temporaire en production — ils expirent après 60 jours
404 Not FoundMauvais Phone Number ID dans l'URL ; numéro non enregistré sur Cloud APIVérifiez le Phone Number ID dans Meta Developer Dashboard → WhatsApp → Phone Numbers. Confirmez que le numéro est enregistré et vérifié.
429 Too Many RequestsLimite de 80 msg/s dépassée, ou rate limit d'appels APIImplémentez un backoff exponentiel avec jitter. Ajoutez un délai de 15–20 ms entre envois lors d'opérations en masse. Ne réessayez pas immédiatement.
460 (interne Meta)Le numéro du destinataire n'est pas enregistré sur WhatsAppLe numéro n'est pas un utilisateur WhatsApp. Retirez-le de votre liste. Ce n'est pas un numéro livrable.
131026Message non livrable — restrictions sur le compte WhatsApp du destinataireLe destinataire a peut-être bloqué votre numéro ou son compte est restreint. Loguez et passez.
500 / 503Panne d'infrastructure Meta ou erreur temporaireRéessayez avec backoff exponentiel. Vérifiez metastatus.com pour les incidents en cours. Ne réessayez pas plus de 3 fois.

Classe de client WhatsApp complète (Node.js)

Les fonctions ci-dessus regroupées en classe réutilisable avec logique de retry intégrée, gestion d'erreurs cohérente et configuration basée sur les variables d'environnement :

Node.js
WhatsAppClient.js
class WhatsAppClient { constructor({ phoneId = process.env.WA_PHONE_ID, token = process.env.WA_TOKEN, version = 'v21.0', } = {}) { this.baseUrl = `https://graph.facebook.com/${version}/${phoneId}/messages`; this.headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }; } async #send(payload) { const res = await fetch(this.baseUrl, { method: 'POST', headers: this.headers, body: JSON.stringify({ messaging_product: 'whatsapp', ...payload }), }); if (!res.ok) { const err = await res.json(); throw Object.assign(new Error(err?.error?.message ?? 'WhatsApp API error'), { code: err?.error?.code, status: res.status, }); } return (await res.json()).messages[0].id; } text(to, body) { return this.#send({ to, type: 'text', text: { body } }); } image(to, link, caption = '') { return this.#send({ to, type: 'image', image: { link, caption } }); } document(to, link, filename, caption = '') { return this.#send({ to, type: 'document', document: { link, filename, caption } }); } location(to, lat, lng, name = '', address = '') { return this.#send({ to, type: 'location', location: { latitude: lat, longitude: lng, name, address } }); } buttons(to, bodyText, buttons) { return this.#send({ to, type: 'interactive', interactive: { type: 'button', body: { text: bodyText }, action: { buttons: buttons.map(b => ({ type: 'reply', reply: { id: b.id, title: b.label.slice(0, 20) } })) }, }}); } template(to, name, lang, variables = []) { return this.#send({ to, type: 'template', template: { name, language: { code: lang }, components: variables.length ? [{ type: 'body', parameters: variables.map(v => ({ type: 'text', text: v })) }] : [], }}); } } // Utilisation const wa = new WhatsAppClient(); await wa.text('+15550001234', 'Hello! 👋'); await wa.template('+15550001234', 'order_shipped', 'en', ['#1234']); await wa.buttons('+15550001234', 'Rate your experience:', [{ id: 'great', label: 'Great! 🌟' }, { id: 'poor', label: 'Could be better' }]); module.exports = WhatsAppClient;

L'autre moitié : recevoir les réponses avec SocialHook

Envoyer des messages n'est que la moitié du tableau. Quand votre client répond, la WhatsApp Cloud API déclenche un webhook vers votre serveur — mais vous avez besoin d'un endpoint HTTPS accessible publiquement pour le recevoir, plus de la vérification de signature HMAC-SHA256, de l'extraction de payload imbriqué et de la gestion des retry.

SocialHook prend en charge toute la couche entrante. Connectez votre numéro WhatsApp à SocialHook, collez l'URL de votre serveur comme destination, et chaque réponse client arrive comme un événement JSON normalisé — vérifié, extrait de l'enveloppe imbriquée de Meta et transféré à votre endpoint en moins de 50 ms. Le même format plat fonctionne aussi pour Facebook Messenger et Instagram DMs.

La configuration complète : votre serveur utilise le WhatsAppClient ci-dessus pour l'envoi, SocialHook livre les événements entrants à votre handler webhook. Deux directions, un seul forfait à 50 $/mois. Voir le guide complet de webhook entrant ou commencer avec le quickstart de 5 minutes.

Questions fréquentes

Comment envoyer un message WhatsApp en utilisant la Cloud API ?
POST vers https://graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages avec votre header Authorization: Bearer {ACCESS_TOKEN} et un body JSON avec messaging_product: "whatsapp", to: "+E.164_number", type: "text", et text: { body: "message" }. Le code complet pour Node.js, Python et PHP est dans la section message texte ci-dessus.
Qu'est-ce que le Phone Number ID WhatsApp et où le trouver ?
Le Phone Number ID est un identifiant numérique pour votre numéro spécifique WhatsApp Business sur la Cloud API — il est différent du numéro de téléphone réel. Trouvez-le dans Meta Developer Dashboard → WhatsApp → Phone Numbers. Il ressemble à un nombre à 15–16 chiffres. Utilisez-le dans l'URL : graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages. Un numéro de téléphone a un ID qui ne change jamais.
Quelle est la différence entre un message de session et un message modèle ?
Un message de session (texte, image, boutons, localisation, document) ne peut être envoyé que dans la fenêtre de service de 24 heures qui s'ouvre quand un client vous écrit en premier. Ceux-ci sont gratuits dans le quota mensuel. Un message modèle est un format approuvé par Meta requis pour initier de nouvelles conversations ou envoyer hors de la fenêtre de service. Les modèles doivent être approuvés (1–24 h pour utility, jusqu'à 5 jours pour marketing) avant de pouvoir être envoyés.
Que se passe-t-il quand je reçois un 429 de la WhatsApp Cloud API ?
Vous avez atteint la rate limit (80 msg/s par défaut). Implémentez un backoff exponentiel : attendez 1000 * 2^attempt + random(500) ms avant de réessayer, plafonné à 60 secondes. Ajoutez du jitter (le composant aléatoire) pour empêcher les retries synchronisés de plusieurs workers. Pour les campagnes en masse, ajoutez un délai fixe de 15–20 ms entre envois pour rester en sécurité sous la limite plutôt que de l'atteindre puis réessayer.
Comment envoyer un modèle WhatsApp avec variables en Python ?
Mettez "type": "template" et incluez un tableau components avec un composant body contenant parameters — un objet par variable dans votre modèle. Chaque paramètre est {"type": "text", "text": "variable_value"}. Ils correspondent séquentiellement à {{1}}, {{2}}, {{3}} dans votre modèle. Le code Python complet est dans la section message modèle ci-dessus.
Comment recevoir les réponses des clients aux messages WhatsApp ?
Les réponses des clients arrivent via webhook — Meta déclenche un HTTP POST vers votre endpoint enregistré lorsqu'un événement de message se produit. Vous avez besoin d'une URL HTTPS accessible publiquement, de la vérification de signature HMAC-SHA256 et du parsing de payload imbriqué. SocialHook gère tout cela automatiquement et livre du JSON propre à votre endpoint en moins de 50 ms. Voir la configuration complète dans notre guide de webhook entrant.

Vous pouvez envoyer. Maintenant
recevez aussi les réponses.

Votre code sortant est prêt. Connectez SocialHook pour l'entrant — chaque réponse client arrive comme JSON propre à votre endpoint webhook. Aucun boilerplate HMAC, aucun parsing de payload imbriqué, aucune infrastructure de retry à construire.

Sans carte bancaire · 50 $/mois après essai · Annulez à tout moment