---
title: "Envoyer un message WhatsApp par API : cURL, Node, Python, PHP"
description: "Comment envoyer un message WhatsApp par API : exemples cURL, Node.js, Python et PHP pour les modèles et les messages de session, suivi des statuts et erreurs."
canonical: "https://omnichannels.ai/fr/blog/send-whatsapp-message-api-examples/"
language: "fr"
updated: "2026-10-16"
publisher: "BroadNet Technologies"
brand: "OmniChannels.ai"
category: "integration"
primaryQuery: "envoyer message whatsapp api"
keywords: ["exemple api whatsapp curl", "envoyer modèle whatsapp node.js", "api whatsapp python", "api whatsapp php", "api whatsapp business envoyer un message", "message de session whatsapp api", "api whatsapp multipart form-data"]
published: "2026-10-16"
author: "OmniChannels.ai Editorial Team"
reviewer: "BroadNet Messaging Solutions Team"
readingMinutes: 10
answer: "Avec l'API OmniChannels.ai, vous envoyez tous les messages WhatsApp via un seul point de terminaison, POST https://api.broadnet.me/whatsapp/message/sendbulk, en données de formulaire multipart avec un en-tête Authorization: Bearer. Le champ messageType sélectionne la charge utile : TEMPLATE pour les modèles approuvés, nécessaires pour démarrer une conversation ou écrire hors de la fenêtre de 24 heures, et TEXT, IMAGE ou INTERACTIVE pour les réponses à l'intérieur de celle-ci."
takeaways: ["Tous les messages WhatsApp passent par POST /whatsapp/message/sendbulk ; messageType (TEMPLATE, TEXT, IMAGE, INTERACTIVE et autres) détermine les champs obligatoires.", "La requête est en multipart/form-data, pas en JSON : Python a donc besoin de files= et PHP d'un corps sous forme de tableau pour éviter l'encodage URL.", "Hors de la fenêtre de service client de 24 heures, seul un modèle approuvé peut être envoyé ; à l'intérieur, vous pouvez envoyer du texte libre, des médias et des messages interactifs.", "L'appel met les messages en file d'attente et renvoie immédiatement un batchId ; les statuts par destinataire arrivent sur votre webhook et via le point de terminaison bulk-insights.", "L'API n'accepte pas de clé d'idempotence : enregistrez chaque envoi avant de l'effectuer et vérifiez le statut après un délai d'attente dépassé plutôt que de renvoyer."]
faq: 
  - q: "Envoie-t-on les messages WhatsApp en JSON ou en données de formulaire ?"
    a: "En données de formulaire. Le point de terminaison d'envoi WhatsApp, POST /whatsapp/message/sendbulk, accepte du multipart/form-data, chaque champ étant une partie du formulaire. Les envois SMS unitaires utilisent du JSON, source fréquente de confusion. En Python, envoyez les champs via files= plutôt que data=, et en PHP passez un tableau comme corps pour que cURL construise du multipart."
  - q: "Peut-on envoyer un message WhatsApp sans modèle ?"
    a: "Uniquement dans la fenêtre de service client. Lorsqu'un client vous écrit, une fenêtre de 24 heures s'ouvre et vous pouvez répondre par du texte, des médias ou des messages interactifs. En dehors, ou pour démarrer une conversation, vous devez envoyer un modèle approuvé. L'API applique les règles de Meta, et un envoi en texte libre vers une fenêtre fermée échouera."
  - q: "Comment savoir si le message a été délivré ?"
    a: "L'appel d'envoi confirme seulement que les messages ont été mis en file d'attente et renvoie un batchId. Les statuts par destinataire passent par SUBMITTED, SENT, DELIVERED et READ, ou se terminent en FAILED ou REJECTED. Ils sont poussés vers votre webhook, et vous pouvez aussi les lire par lot via le point de terminaison bulk-insights."
  - q: "Peut-on envoyer à de nombreux destinataires en une seule requête ?"
    a: "Oui. destinationNo accepte plusieurs numéros séparés par des virgules, des points-virgules ou des retours à la ligne, et destinationFiles accepte un fichier TXT, CSV ou XLSX. Pour un modèle avec des variables différentes par destinataire, importez un fichier personnalisé avec une ligne par destinataire. Réglez allowDuplicate sur false pour supprimer les numéros en double."
  - q: "Existe-t-il une clé d'idempotence pour éviter les envois en double ?"
    a: "Non. L'API n'accepte pas de clé d'idempotence. Si une requête dépasse le délai d'attente, le client ne peut pas savoir si elle a été acceptée. Enregistrez chaque envoi dans votre propre base avant l'appel, stockez le batchId renvoyé et vérifiez le statut ou la liste des lots avant tout nouvel essai."
  - q: "Faut-il un opt-in avant d'envoyer ?"
    a: "Oui. La politique de Meta exige que chaque destinataire ait donné son opt-in pour recevoir des messages de votre entreprise, et les modèles doivent être approuvés avant usage. L'API ne vérifie pas le consentement à votre place. Recueillez l'opt-in à la source, conservez la preuve et excluez toute personne qui se désinscrit."
sources: 
  - title: "Politique de messagerie WhatsApp Business"
    url: "https://business.whatsapp.com/policy"
  - title: "Tarification de la plateforme WhatsApp Business — Meta for Developers"
    url: "https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing"
  - title: "RFC 7578 : Returning Values from Forms: multipart/form-data"
    url: "https://www.rfc-editor.org/rfc/rfc7578"
---

# Envoyer un message WhatsApp par API : cURL, Node, Python, PHP

Canonical: https://omnichannels.ai/fr/blog/send-whatsapp-message-api-examples/
Language: fr
Publisher: OmniChannels.ai by BroadNet Technologies

Ce guide s'adresse aux développeurs qui intègrent l'envoi WhatsApp dans une application. Il présente des requêtes fonctionnelles en cURL, Node.js, Python et PHP, explique la différence entre messages modèles et messages de session, et traite du suivi et des erreurs. Chaque point de terminaison et chaque champ proviennent de notre [référence de l'API WhatsApp](/developers/whatsapp/) et de la spécification OpenAPI.

## Comment envoyer un message WhatsApp via l'API ?

Vous effectuez un seul `POST` authentifié vers `https://api.broadnet.me/whatsapp/message/sendbulk` avec des champs de formulaire. Le champ `messageType` choisit le type de message, et la réponse confirme que les messages ont été mis en file d'attente.

| Élément | Valeur |
|---|---|
| URL de base | `https://api.broadnet.me` (sans préfixe `/v1`) |
| Point de terminaison | `POST /whatsapp/message/sendbulk` |
| Authentification | `Authorization: Bearer $BROADNET_TOKEN` |
| Type de contenu | `multipart/form-data` (défini automatiquement par votre client HTTP) |
| Réponse | Résumé de lot en JSON avec un `batchId` |

Les identifiants de test sont délivrés après une brève demande d'accès, et les envois de test partent vers vos propres numéros vérifiés. Voir le [démarrage rapide](/developers/quickstart/) et les pages d'authentification. Gardez le jeton uniquement côté serveur.

## Modèle ou message de session : lequel envoyer ?

Envoyez un modèle pour démarrer une conversation ou écrire hors de la fenêtre, et un message de session lorsque le client vous a écrit au cours des dernières 24 heures. Cette règle est fixée par Meta, pas par nous.

| | Message modèle | Message de session |
|---|---|---|
| `messageType` | `TEMPLATE` | `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`, `INTERACTIVE` et autres |
| Quand c'est autorisé | À tout moment, une fois le modèle approuvé | Uniquement tant que la fenêtre de service client est ouverte |
| Approbation requise | Oui, par Meta | Non |
| Usage typique | Suivis de commande, OTP, rappels, campagnes | Réponses du support, réponses aux questions |

Meta facture chaque message délivré selon la catégorie, et ses règles sur les messages de service et les modèles utilitaires ont changé le 1er octobre 2026. Notre [guide de la fenêtre](/fr/blog/whatsapp-24-hour-customer-service-window/) en détaille la mécanique, et notre [page tarifs](/fr/whatsapp-pricing/) et notre [calculateur](/fr/tools/whatsapp-pricing-calculator/) traitent du coût.

## Comment envoyer un modèle approuvé ?

Réglez `messageType=TEMPLATE`, nommez le modèle et son code de langue, et transmettez les valeurs des variables. Créez `batchId=0` pour démarrer un nouveau lot.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F campaignName=order-updates \
  -F destinationNo=971500000000 \
  -F messageType=TEMPLATE \
  -F templateName=order_update \
  -F templateLanguage=en_US \
  -F bodyTemplateVariables=INV-400
```

```js
// Node.js 18+ (fetch et FormData intégrés)
const form = new FormData();
form.append("batchId", "0");
form.append("campaignName", "order-updates");
form.append("destinationNo", "971500000000");
form.append("messageType", "TEMPLATE");
form.append("templateName", "order_update");
form.append("templateLanguage", "en_US");
form.append("bodyTemplateVariables", "INV-400");

const res = await fetch("https://api.broadnet.me/whatsapp/message/sendbulk", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.BROADNET_TOKEN}` }, // fetch définit la frontière multipart
  body: form,
});
console.log(await res.json());
```

```python
import os
import requests

fields = {
    "batchId": "0",
    "campaignName": "order-updates",
    "destinationNo": "971500000000",
    "messageType": "TEMPLATE",
    "templateName": "order_update",
    "templateLanguage": "en_US",
    "bodyTemplateVariables": "INV-400",
}
# Envoyez chaque champ comme une partie multipart (files=) plutôt qu'encodé en URL (data=).
r = requests.post(
    "https://api.broadnet.me/whatsapp/message/sendbulk",
    headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
    files={k: (None, v) for k, v in fields.items()},
    timeout=30,
)
print(r.json())
```

```php
<?php
$ch = curl_init("https://api.broadnet.me/whatsapp/message/sendbulk");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BROADNET_TOKEN")],
    // Un tableau (et non une chaîne) fait envoyer du multipart/form-data par cURL.
    CURLOPT_POSTFIELDS => [
        "batchId" => "0",
        "campaignName" => "order-updates",
        "destinationNo" => "971500000000",
        "messageType" => "TEMPLATE",
        "templateName" => "order_update",
        "templateLanguage" => "en_US",
        "bodyTemplateVariables" => "INV-400",
    ],
]);
echo curl_exec($ch);
curl_close($ch);
```

La destination est l'indicatif pays suivi du numéro, uniquement des chiffres. L'appel répond immédiatement avec un résumé de lot :

```json
{
  "totalFiles": 1,
  "totalSuccess": 1,
  "totalFailed": 0,
  "totalDuplicates": 0,
  "batchId": "2609091405000000001",
  "fileResults": [
    {
      "fileName": "direct",
      "totalRecords": 1,
      "successCount": 1,
      "failedRecipients": [],
      "duplicateRecipients": []
    }
  ],
  "message": "Messages queued for delivery"
}
```

Stockez le `batchId` à côté de votre propre enregistrement. « En file d'attente » ne veut pas dire « délivré ».

## Quels champs de la requête comptent le plus ?

Les champs obligatoires dépendent de `messageType`. Voici les plus courants.

| Champ | Obligatoire | Description |
|---|---|---|
| `messageType` | Oui | `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`, `STICKER`, `LOCATION`, `CONTACTS`, `TEMPLATE`, `INTERACTIVE`, `REACTION` ou `FLOW` |
| `destinationNo` ou `destinationFiles` | L'un des deux | Numéros séparés par des virgules, des points-virgules ou des retours à la ligne ; ou un fichier TXT, CSV ou XLSX |
| `batchId` | Non | `0` démarre un nouveau lot |
| `campaignName` | Non | Votre libellé pour le reporting |
| `allowDuplicate` | Non | `false` supprime les destinataires en double |
| `templateName`, `templateLanguage` | Pour `TEMPLATE` | Nom du modèle approuvé et code de langue, comme `en_US` ou `ar` |
| `bodyTemplateVariables` | Selon le modèle | Valeur de la variable du corps ; les paramètres nommés sont transmis en JSON |
| `buttonsTemplateVariable` | Selon le modèle | Valeur dynamique du bouton : suffixe d'URL, code promo ou code OTP |
| `content` | Pour `TEXT` | Corps du texte, jusqu'à 4 096 caractères, ou légende d'un média |
| `mediaUrl` | Selon le type | URL HTTPS publique du média |
| `interactivePayload` | Pour `INTERACTIVE` | Objet interactif de Meta sous forme de chaîne JSON |

La planification utilise `scheduled`, `scheduleTime` et `gmt`, et `delay` limite le rythme de livraison. La liste complète figure dans la [référence de l'API WhatsApp](/developers/whatsapp/).

## Comment envoyer un modèle avec un bouton ou un OTP ?

Ajoutez la valeur du bouton dans `buttonsTemplateVariable`. Pour un modèle d'authentification avec bouton de copie du code, transmettez le code à la fois dans le corps et dans le champ du bouton.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=TEMPLATE \
  -F templateName=login_code \
  -F templateLanguage=en_US \
  -F bodyTemplateVariables=482913 \
  -F buttonsTemplateVariable=482913
```

Ici, `login_code` est un espace réservé pour votre propre modèle d'authentification approuvé. Pour plusieurs boutons, `buttonsTemplateVariable` accepte un tableau JSON. Générez le code côté serveur, stockez-le haché avec une expiration et ne le journalisez jamais. Notre [guide des OTP](/fr/blog/otp-authentication-messages-guide/) couvre la livraison en un appui, sans appui et par copie de code.

## Comment répondre dans la fenêtre de service client ?

Utilisez un type de message de session. Une réponse en texte simple nécessite `messageType=TEXT` et `content`.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=TEXT \
  -F campaignName=support \
  -F "content=Your order has shipped. Reply here if you need anything."
```

Pour des boutons de réponse, envoyez `messageType=INTERACTIVE` et un `interactivePayload` conforme au format des messages interactifs de Meta.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=INTERACTIVE \
  -F campaignName=support \
  -F 'interactivePayload={"type":"button","body":{"text":"Confirm your delivery slot?"},"action":{"buttons":[{"type":"reply","reply":{"id":"slot_ok","title":"Confirm"}},{"type":"reply","reply":{"id":"slot_change","title":"Change"}}]}}'
```

Pour marquer un message entrant comme lu et, facultativement, afficher un indicateur de saisie, appelez `POST /whatsapp/message/read` avec du JSON :

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/read \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "messageId": "wamid.HBgMOTcxNTAwMDAwMDAwFQIAEhgg", "typing": true }'
```

Consultez notre guide des messages interactifs pour des conseils de conception.

## Comment suivre la livraison ?

Les statuts sont poussés vers le webhook configuré sur votre compte et peuvent aussi être lus par lot. Chaque destinataire passe par `SUBMITTED`, `SENT`, `DELIVERED` et `READ`, ou se termine en `FAILED` ou `REJECTED` avec un motif.

```bash
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&status=FAILED&page=0&size=50" \
  -H "Authorization: Bearer $BROADNET_TOKEN"
```

`batch_Id` est obligatoire ; `status`, `destination`, `search`, `page` et `size` sont facultatifs, et `page` commence à zéro. Utilisez l'interrogation en complément pour rattraper les événements manqués, et construisez votre récepteur comme décrit dans notre [guide des webhooks](/fr/blog/whatsapp-webhooks-guide/). Les lots peuvent aussi être mis en pause, repris et abandonnés via les points de terminaison de lots de la référence.

## Quelles erreurs faut-il gérer ?

Lisez d'abord le statut HTTP, puis le corps de l'erreur. L'API renvoie les erreurs dans un corps JSON à cinq champs, et les échecs propres à WhatsApp peuvent venir de Meta.

| Statut | Signification | Action |
|---|---|---|
| `400` | Erreur de validation, par exemple un champ propre au type manquant ou une destination invalide | Corriger la requête ; ne pas réessayer telle quelle |
| `401` / `403` | Jeton manquant ou invalide, ou opération non autorisée | Corriger le jeton ou nous demander d'activer l'opération |
| `404` | Modèle ou expéditeur introuvable | Vérifier le nom du modèle et la langue |
| `413` | Fichier envoyé trop volumineux | Réduire le fichier |
| `429` | Limite de débit | Attendre `Retry-After` |
| `502` | Meta a rejeté l'appel ; `message` contient le code et le texte d'erreur de Meta | Le lire ; ne réessayer que si l'erreur est transitoire |
| `500`, `503`, `504` | Problème de serveur ou en amont | Réessayer avec backoff exponentiel et jitter |

Notre guide des codes d'erreur explique les codes Meta courants. Faute de clé d'idempotence, suivez les étapes de protection contre les doublons décrites dans [Erreurs et limites](/developers/errors-and-limits/) : écrivez d'abord un enregistrement « en attente », stockez le `batchId` renvoyé et, après un délai d'attente dépassé, vérifiez le statut avant de renvoyer.

## Exemple concret : un service de notification de commandes

« Souq Rafiq » (société fictive), enseigne de distribution aux Émirats arabes unis, envoie un modèle `order_update` à l'expédition d'une commande.

1. Le service de commandes écrit une ligne de clé `order-8842-shipped` avec le statut `pending`.
2. Il appelle `sendbulk` avec le modèle et le numéro de commande comme `bodyTemplateVariables`.
3. Sur un `200`, il stocke le `batchId` et marque la ligne `accepted`.
4. Le gestionnaire de webhook met à jour la ligne en `DELIVERED` ou `READ`, ou en `FAILED` avec un motif.
5. Si le client répond, l'outil de support utilise un message de session `TEXT` dans la fenêtre.
6. Une tâche de nuit lit `bulk-insights` pour les lots de la veille et rapproche les statuts manquants.

## Quelles sont les erreurs courantes ?

- **Envoyer du JSON.** Le point de terminaison d'envoi WhatsApp est en multipart ; `Content-Type: application/json` ne fonctionnera pas.
- **`data=` en Python** au lieu de `files=`, ce qui encode le formulaire en URL.
- **Texte libre hors de la fenêtre,** qui échoue. Utilisez un modèle.
- **Mauvais code de langue,** par exemple `en` au lieu du `en_US` du modèle.
- **Considérer `200` comme « délivré ».** Cela signifie « en file d'attente ».
- **Nouveaux essais à l'aveugle** après un délai dépassé, qui peuvent écrire deux fois au client.
- **Jeton dans le code client.** Appelez l'API uniquement depuis votre backend.
- **Limites :** les détails de la charge utile varient selon le modèle. Confirmez la structure des variables avec notre équipe pour votre première campagne personnalisée.

## Comment OmniChannels.ai vous aide

BroadNet est un Meta Business Solution Provider officiel, et l'onboarding vers l'API WhatsApp Business passe par l'Embedded Signup de Meta ; notre équipe prépare l'enregistrement avec vous, les approbations restant du ressort de Meta. L'API REST est documentée par une spécification OpenAPI 3.1 et un SDK TypeScript, `@broadnet/cpaas-sdk`, et les statuts de livraison sont normalisés entre les canaux. Commencez par la [documentation de l'API WhatsApp](/developers/whatsapp/) ou le [démarrage rapide](/developers/quickstart/).
