Envoyer des messages WhatsApp
Ce guide présente le point de terminaison d'envoi, POST /v1/whatsapp/messages. Vous construisez une charge utile JSON avec un destinataire et exactement un type de contenu : un modèle préapprouvé, ou un message de service contenant du texte, une image, une vidéo, un audio, un sticker, un document, une position, des fiches de contact ou un élément sur lequel appuyer. Bird renvoie 202 Accepted avec un identifiant de message et effectue la livraison de manière asynchrone. Le type que vous pouvez envoyer dépend de la fenêtre de service client. Chaque requête envoie un message à un seul destinataire, et il n'existe aucun point de terminaison d'envoi groupé.
Un envoi minimal
La plus petite charge utile valide se compose d'un destinataire to et d'un template avec son slug. Ajoutez language si vous voulez une langue précise ; sans ce champ, le modèle est envoyé dans sa langue par défaut. Renseignez les variables déclarées par le modèle via parameters.
L'appel curl utilise l'hôte américain ; si votre clé commence par bk_eu1_, appelez plutôt https://eu1.platform.bird.com. Les SDK déduisent la région de votre clé et ne définissent donc aucun hôte.
const msg = await bird.whatsapp.send({
to: "+15551234567",
template: {
slug: "bird_otp",
parameters: { code: "123456" },
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+15551234567",
template="bird_otp",
language="en",
parameters={"code": "123456"},
)
print(msg.id, msg.status)msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "+15551234567",
Template: "bird_otp",
Language: "en",
Parameters: map[string]any{"code": "123456"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)$message = $bird->whatsapp->send(
to: '+15551234567',
template: 'bird_otp',
language: 'en',
parameters: ['code' => '123456'],
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--language en \
--parameters '{"code":"123456"}' \
--template bird_otp \
--to +15551234567curl -X POST "https://{region}.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "+15551234567",
"template": {
"slug": "bird_otp",
"language": "en",
"parameters": {
"code": "123456"
}
}
}'La fenêtre de service client
Le type de message que vous pouvez envoyer dépend d'un seul état : la fenêtre de service client est-elle ouverte ?
Le contact ouvre la fenêtre en envoyant un message ou en appelant votre numéro professionnel. Elle reste ouverte 24 heures et repart de zéro à chaque nouveau message de sa part. Tant qu'elle est ouverte, vous pouvez envoyer un message de service, c'est-à-dire n'importe quel contenu libre : texte, image, vidéo, audio, sticker, document, position, fiches de contact ou contenu interactif. Une fois qu'elle a expiré, seul un modèle préapprouvé lui parvient, et sa réponse à ce modèle rouvre la fenêtre.
Bird suit la fenêtre pour vous : un message de service envoyé dans une fenêtre fermée est refusé avant toute création ou facturation, et la requête renvoie une erreur 422 E15044 WhatsAppServiceWindowClosed. Cette vérification est faite au mieux et laisse passer en cas de doute. Un 202 ne prouve donc pas que la fenêtre était réellement ouverte lors de l'expédition. Si la fenêtre expire entre l'acceptation et l'expédition, l'échec survient de manière asynchrone, avec service_window_expired dans le champ last_error du message.
Consultez la fenêtre de service client pour le cycle de vie complet : ce qui l'ouvre, ce qui la réinitialise et son lien avec la tarification.
Construire la charge utile
Destinataire
to désigne une seule destination, sous la forme d'un numéro de téléphone, d'un identifiant utilisateur propre à l'entreprise ou d'un identifiant de groupe. Un numéro de téléphone est au format E.164 : un + initial, l'indicatif du pays, puis le numéro d'abonné, par exemple +14155550100. Nous validons le numéro : une valeur qui ne peut pas être un numéro réel et joignable (longueur incorrecte, préfixe non attribué) est rejetée avec une 422 WhatsAppInvalidRecipient avant toute facturation. Il n'existe ni tableau de destinataires ni envoi groupé : pour joindre plusieurs personnes qui ne font pas partie d'un même groupe, vous effectuez un appel par destinataire.
Un identifiant utilisateur propre à l'entreprise, par exemple US.13491208655302741918, permet d'écrire à un contact dont vous n'avez pas le numéro de téléphone. C'est ainsi que vous répondez à un contact qui vous a joint sans numéro. Deux choses changent : le numéro d'envoi doit appartenir au même portefeuille d'entreprise que celui auquel l'identifiant est rattaché, et un modèle de code à usage unique exige un numéro de téléphone. Un modèle géré par Bird est refusé dès l'acceptation avec une 422 WhatsAppRecipientNotSupportedForTemplate ; un modèle d'authentification créé par votre espace de travail est accepté puis échoue, car Meta exige un numéro de téléphone pour ce type de modèle.
to accepte une autre forme : un identifiant de groupe WhatsApp, par exemple wag_01krdgeqcxet5s7t44vh8rt9mg, qui envoie le message à tous les participants de cette discussion de groupe. Un envoi de groupe omet from et rend compte de la distribution pour l'ensemble du groupe plutôt que pour un seul destinataire. C'est pourquoi Envoyer à un groupe WhatsApp le traite sur une page dédiée.
Modèle
template désigne le modèle préapprouvé à envoyer :
slug(obligatoire) : l'identifiant court du modèle, par exemplebird_order_confirmation. Il doit correspondre à un modèle de votre catalogue (lettres minuscules, chiffres et traits de soulignement).language: la balise de langue du modèle, par exempleenoupt-BR. Omettez-la pour envoyer la langue par défaut du modèle. Si vous indiquez une langue que le modèle ne possède pas, la requête renvoie une422qui liste les langues disponibles. Le message accepté renvoie la langue retenue.parameters: les valeurs des variables du modèle, sous la forme d'un objet unique indexé par nom (voir Composants et paramètres). Omettez-le pour un modèle sans variables.components: les mêmes valeurs au format de composants de Meta, à la place deparameters. Quelques modèles n'acceptent que cette forme.
Consultez vos modèles, leurs langues et un aperçu de chacun sur la page Templates.
Composants et paramètres
Les modèles contiennent des variables, nommées ({{ref}}, {{amount}}) ou numérotées ({{1}}, {{2}}). Renseignez-les dans parameters, un seul objet. Le champ variables de la version du modèle liste les clés qu'il accepte et indique celles qui sont facultatives. Chaque clé correspond à l'un des cas suivants :
- le nom d'une variable nommée, par exemple
order_id - le numéro d'une variable numérotée, par exemple
1 header_url,header_filenameouheader_locationpour un en-tête média ou de localisation, etheader_url_1,header_url_2, etc. pour l'en-tête d'une carte de carrouselbutton_urlpour le suffixe numéroté d'un bouton URL, etbutton_url_1,button_url_2, etc. pour le bouton d'une carte de carrousel. Un suffixe nommé utilise son propre nom.codepour le code d'un modèle d'authentification et pour la valeur d'un bouton de copie du code- une entrée lue par les liaisons de paramètres de la langue
Chaque valeur est une chaîne, un nombre ou un booléen. header_location accepte un objet avec latitude et longitude. Si vous omettez un en-tête média, l'exemple approuvé du modèle est envoyé. Un modèle importé depuis Meta n'a pas d'exemple : il nécessite donc la clé de l'en-tête média. Les clés que la langue n'utilise pas sont ignorées.
{
"to": "+15551234567",
"from": "+13124495648",
"template": {
"slug": "order_update",
"language": "en",
"parameters": { "order_id": "A-4192", "first_name": "Ava" }
}
}Avec le CLI, transmettez l'objet avec --parameters :
bird whatsapp send --to +15551234567 --from +13124495648 \
--template order_update --language en \
--parameters '{"order_id":"A-4192","first_name":"Ava"}'Un modèle d'authentification dont le corps indique {{1}} is your verification code et dont le bouton copie le code reçoit le code une seule fois, sous la forme code. Bird l'insère à la fois dans le corps et dans le bouton :
{
"parameters": { "code": "481920" }
}Une langue dont les liaisons lisent des données de contact, comme bird.contact.first_name, ne peut pas être envoyée en message individuel et renvoie E15091. Envoyez-la plutôt dans une diffusion, où le contact de chaque destinataire fournit les valeurs. Une diffusion ignore un destinataire dont les données de contact laissent une valeur obligatoire vide. Envoyez parameters ou components, mais pas les deux. Certains modèles n'acceptent que parameters : ceux dont la langue calcule ses valeurs avec des liaisons de paramètres, et ceux qui ont des variables nommées dans une carte de carrousel ou dans l'URL d'un bouton.
Certains modèles n’acceptent que components, car une seule clé ne permet pas de distinguer leurs emplacements. C’est le cas lorsqu’un modèle numérote {{1}} dans plusieurs blocs, par exemple l’en-tête et le corps. C’est aussi le cas lorsqu’un bloc contient deux boutons URL avec des suffixes numérotés, ou une variable {{button_url}} à côté d’un bouton URL numéroté. L’envoi d’un tel modèle avec parameters renvoie 422.
Remplir un modèle par composant. components est l’alternative à parameters, et la seule forme possible pour un modèle qu’une clé unique ne peut pas adresser. Chaque composant indique un type (header, body, button ou carousel) et un tableau parameters. Un carousel prend à la place un tableau cards, avec une entrée par carte approuvée, dans l’ordre d’approbation. Chaque carte contient son propre tableau components d’entrées header, body et button. Chaque paramètre indique son propre type (text, image, video, gif, document ou location) et contient le champ correspondant : pour text, une simple chaîne ; pour image/video/gif/document, une url https publique ; pour location, un point sur la carte. Un modèle à paramètres nommés exige un name sur chaque paramètre, correspondant exactement aux noms déclarés par le modèle (voir Référence des champs). Un modèle positionnel omet name et reçoit ses valeurs dans l’ordre de {{n}} : le premier paramètre remplit donc {{1}}. Dans les deux cas, des paramètres qui ne correspondent pas aux déclarations du modèle renvoient une WhatsAppTemplateParameterMismatch 422. Sur un modèle géré par Bird, un composant header est ignoré, car aucun modèle géré par Bird ne déclare de variable d’en-tête ; sur un modèle créé par votre espace de travail, il est transmis, et c’est ainsi qu’un modèle utilitaire ou marketing avec en-tête média reçoit son image.
Le même modèle de code de vérification reçoit le code à la fois comme paramètre du corps et comme paramètre du bouton, de manière positionnelle (sans name) :
{
"components": [
{ "type": "body", "parameters": [{ "type": "text", "text": "481920" }] },
{ "type": "button", "parameters": [{ "type": "text", "text": "481920" }] }
]
}Catégorie et expéditeur
La catégorie d’un modèle (authentication, utility ou marketing) détermine la manière dont WhatsApp traite le message et, avec le pays de destination, son coût.
Le propriétaire de l’expéditeur détermine si vous devez l’indiquer :
- Un modèle géré par Bird (son slug commence par
bird_) est envoyé depuis le numéro que Bird réserve à cette catégorie : omettez doncfrom. Si vous le renseignez, la requête renvoie uneWhatsAppSenderNotAllowed422. - Tout autre envoi indique son propre expéditeur dans
from: un message de service de n’importe quel type, et tout modèle créé par votre espace de travail. Le numéro doit appartenir à votre espace de travail. Si vous l’omettez, la requête renvoie uneWhatsAppSenderRequired422, et un numéro depuis lequel l’espace de travail ne peut pas envoyer renvoie uneWhatsAppSenderNotFound422. Un modèle créé par votre espace de travail doit aussi se trouver sur le même compte WhatsApp Business que le numéro ; sinon, l’envoi renvoie uneWhatsAppSenderWABAMismatch422.
La page Configuration des numéros de téléphone présente les deux types de numéros et explique comment connecter votre propre numéro.
Messages de service
Au lieu de template, incluez exactement un des champs suivants : text, image, video, audio, sticker, document, location, contact_cards ou interactive. Ces neuf types sont tous des messages de service : ils nécessitent donc une fenêtre de service client ouverte. Chacun exige aussi from, un numéro appartenant à votre espace de travail ; les numéros gérés par Bird ne peuvent pas les envoyer.
text:{ "body": "..." }, jusqu’à 4 096 caractères. Ajoutez"preview_url": truepour afficher un aperçu du premier lien présent dansbody.image,video,audio,sticker,document: chacun prend une URLhttpspublique que WhatsApp récupère au moment de l'envoi (url) ; une URL signée doit donc rester valide au-delà de l'envoi. Une URLhttpest rejetée d'emblée. WhatsApp récupère lui-même le fichier : une URL inaccessible, une URL qui sert un type non pris en charge ou un fichier qui dépasse la taille maximale de son type est donc accepté, puis échoue, avecmedia_rejecteddans lelast_errordu message et la raison propre à WhatsApp dansdescription.image,videoetdocumentacceptent aussi uncaptionfacultatif ;documentaccepte aussi unfilenamefacultatif ;audioaccepte un indicateurvoicefacultatif pour un affichage sous forme de note vocale.location:{ "latitude": ..., "longitude": ... }(tous deux obligatoires, en degrés décimaux), plusnameetaddressfacultatifs.contact_cards: un tableau de cinq contacts au maximum, partagés dans un seul message. Lenamede chaque fiche exigeformatted_nameet au moins un autre élément (first_name,last_name,middle_name,prefixousuffix) ;phone_numbers,emails,urlsetaddressesacceptent chacun dix entrées au maximum, etorgetbirthday(sous la formeYYYY-MM-DD) sont facultatifs. Unphone_numberau format E.164 ajoute à la fiche un bouton qui ouvre une conversation avec ce numéro.interactive: un texte de corps et un élément sur lequel appuyer, parmi six types : boutons de réponse, menu de liste, bouton de lien, carrousel de médias, ou bouton unique demandant au destinataire sa position ou son numéro de téléphone. Messages interactifs décrit le format de chaque type, les réponses produites par un appui et les limites.
{
"to": "+16505551234",
"from": "+13124495648",
"text": { "body": "Your order shipped: https://example.com/track/A1B2C3", "preview_url": true }
}Une requête sans contenu, ou avec plus d'un type de contenu, est rejetée avec une 422.
Citer un message
Définissez in_reply_to_message_id sur l'ID d'un message WhatsApp pour envoyer votre message en réponse à celui-ci, comme lorsque vous appuyez sur Répondre dans l'application WhatsApp pour citer un message. Le destinataire voit votre message avec le message cité au-dessus, et le champ est renvoyé à chaque lecture du message.
Cela fonctionne aussi dans l'autre sens : un message entrant que WhatsApp marque comme réponse contient l'ID du message cité dans ce même champ, ce qui vous permet de savoir à quel message une réponse répond. Un message entrant que WhatsApp ne marque pas ne contient aucun ID, et la résolution peut aussi échouer. Pour une corrélation fiable, utilisez des identifiants de réponse interactive explicites, associés à l'état de conversation ou de tâche stocké par votre application. Les metadata sortants restent sur l'enregistrement sortant et ne sont pas copiés automatiquement sur la réponse.
La citation est résolue avant l'acceptation de l'envoi : une citation impossible à afficher fait donc échouer la requête elle-même, et rien n'est créé ni facturé. Un ID qui ne désigne aucun message de cet espace de travail, ou un message de plus de 15 jours, durée pendant laquelle un message peut être cité, renvoie une 404 E15071 WhatsAppReferencedMessageNotFound. Un ID désignant un message qui n'a jamais atteint WhatsApp, ou un message d'une autre conversation que celle des to et from de cet envoi, renvoie une 422 E15072 WhatsAppMessageNotQuotable. Si Bird ne peut pas joindre le stockage qui fournit la réponse, l'envoi renvoie une 503 E15073 WhatsAppMessageLookupUnavailable, et une nouvelle tentative est alors utile. La citation fonctionne aussi bien pour un envoi de modèle que pour un envoi libre.
{
"to": "+16505551234",
"from": "+13124495648",
"in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
"text": { "body": "Yes, that slot is still free." }
}Tags et métadonnées
Trois champs facultatifs associent votre propre contexte à un message ; tous trois sont renvoyés lors des lectures API et accompagnent chaque événement webhook du message :
tags: jusqu'à 20 libellés{ "name": ..., "value": ... }structurés, pour les dimensions à faible cardinalité selon lesquelles vous filtrez et établissez vos rapports (une campagne, une variante de test). Les noms et les valeurs acceptent les lettres ASCII, les chiffres, le trait de soulignement et le trait d'union ; les noms sont limités à 32 caractères et uniques au sein d'un envoi, les valeurs à 64. Filtrez la liste des messages par tag (?tag=campaignou?tag=campaign:launch-week) ; la page Metrics ventile la remise par tag.metadata: un objet JSON libre, jusqu'à 2 Ko une fois sérialisé, pour le contexte propre à chaque envoi dont vous n'avez pas besoin comme dimension de filtrage (un identifiant de commande interne, une référence de session).reference: une chaîne de 1 à 255 caractères qui vous permet de retrouver le message, comme un numéro de commande ou de ticket.?reference=sur la liste des messages la compare exactement, en respectant la casse, et plusieurs messages peuvent partager la même valeur. Définissez-la uniquement si vous devez effectuer des recherches dessus, et placez toute autre donnée dansmetadata.
{
"tags": [{ "name": "campaign", "value": "order-confirmations" }],
"metadata": { "order_id": "ord_8271" },
"reference": "ord_8271"
}Référence des champs
| Champ | Type | Obligatoire | Limites / remarques |
|---|---|---|---|
to | chaîne | oui | Un destinataire par message : un numéro de téléphone E.164, un identifiant utilisateur propre à l'entreprise, qu'aucun modèle de code de vérification à usage unique n'accepte, ou un identifiant de groupe WhatsApp (wag_…), qui envoie le message à tous les participants de ce groupe |
from | chaîne (E.164) | non** | À omettre pour un modèle géré par Bird, qui choisit son propre expéditeur, et pour un envoi à un groupe, qui utilise le numéro du groupe ; obligatoire pour un message de service et pour un modèle créé par votre espace de travail, et doit être un numéro appartenant à votre espace de travail |
template.slug | chaîne | non** | Un slug de modèle que votre espace de travail peut envoyer ; les slugs gérés par Bird commencent par bird_ |
template.language | chaîne | non* | Balise de langue du modèle (en, pt-BR) ; omettez-la pour envoyer la langue par défaut du modèle |
template.parameters | objet | non | Remplit les variables du modèle par nom, par exemple ref ou code ; 16 Ko au maximum, et bird est réservé |
template.components | tableau | non | L'alternative à parameters, avec une entrée par composant : type vaut header, body, button ou carousel, et un carousel prend cards au lieu de parameters |
template.components[].parameters[].name | chaîne | non† | L'espace réservé que cette valeur remplit, par exemple ref ; obligatoire pour un modèle à paramètres nommés, où il doit correspondre aux noms déclarés par le modèle, et omis pour un modèle positionnel |
text | objet | non** | Message de service {body, preview_url?} en texte brut (jusqu’à 4 096 caractères) ; voir Texte brut |
image, video, audio, sticker, document | objet | non** | Message de service multimédia avec url (et caption ou filename en option, lorsque le type les prend en charge) ; voir Messages de service |
location | objet | non** | Repère {latitude, longitude, name?, address?} ; voir Localisations |
contact_cards | tableau | non** | Jusqu'à 5 objets de contact vCard structurés ; voir Fiches de contact |
interactive | objet | non** | Texte du corps et un type de contenu sur lequel appuyer ; il s'agit d'un message de service, qui nécessite donc une fenêtre de service ouverte. Voir Messages interactifs |
in_reply_to_message_id | chaîne | non | ID d'un message WhatsApp détenu par cet espace de travail, cité dans le message que vous envoyez ; renvoyé lors des lectures. Voir Citer un message |
tags | tableau | non | Jusqu'à 20 libellés {name, value} ; nom ≤ 32 caractères, valeur ≤ 64, noms uniques |
reference | chaîne | non | 1 à 255 caractères ; correspondance exacte pour ?reference= dans la liste des messages |
metadata | objet | non | JSON arbitraire, jusqu'à 2 Ko une fois sérialisé |
* language est facultatif ; si vous l'omettez, le modèle est envoyé dans sa langue par défaut.
† name est obligatoire sur chaque paramètre d'un modèle à paramètres nommés. Omettez-le pour un modèle positionnel. Consultez Composants et paramètres.
** Incluez exactement un élément parmi template ou un champ de contenu de message de service (text, image, video, audio, sticker, document, location, contact_cards, interactive) ; consultez Messages de service.
Le modèle asynchrone : ce que signifie 202
Un envoi réussi renvoie 202 Accepted avec un ID de message et status: accepted. La réponse 202 n'est renvoyée qu'une fois l'envoi accepté de manière durable : un envoi n'est jamais accepté puis abandonné en silence. Les échecs définitifs que vous pouvez corriger échouent immédiatement avec une 422 : un destinataire invalide, un slug ou une langue de modèle inconnus, des paramètres qui ne correspondent pas, ou un message de service envoyé alors que la fenêtre de service client est fermée (WhatsAppServiceWindowClosed). Un portefeuille sans fonds n'en fait pas partie : l'envoi est accepté, puis le message se termine en rejected avec insufficient_balance lorsque Bird tente de le facturer. La remise effective a lieu de manière asynchrone : le message passe à sent quand nous le transmettons à WhatsApp, puis à un statut final (delivered ou failed) à l'arrivée de l'accusé, signalé par les événements, les webhooks et les endpoints de lecture. Un accusé de lecture apparaît séparément, sous forme d'un horodatage read_at et d'un événement whatsapp.read, et non comme un statut.
Une remarque sur la confidentialité : pour les modèles de catégorie authentication, la réponse API ne renvoie jamais les valeurs renseignées. L'écho 202 et toutes les lectures ultérieures contiennent un tableau components vide pour ces messages, afin qu'un code de vérification ne réapparaisse jamais.
Réessayer en toute sécurité
Envoyez l'en-tête Idempotency-Key avec une valeur unique par envoi logique pour pouvoir réessayer sans risque. Si votre première requête a réussi mais que vous n'avez jamais reçu la réponse (délai dépassé, connexion interrompue), la renvoyer avec la même clé renvoie le résultat d'origine au lieu d'envoyer et de facturer un message en double. La réponse rejouée contient un en-tête Idempotency-Replay. Consultez idempotence pour le format des clés et leur durée de conservation.
Recevoir la réponse
Les messages entrants arrivent sur la même ressource que les messages sortants, et chacun d'eux réinitialise la fenêtre de service. Recevoir des messages WhatsApp explique comment les lire via API, récupérer les médias envoyés par un contact et utiliser le webhook whatsapp.received.
Coût et facturation
WhatsApp est facturé par message, selon la catégorie du modèle et le pays du destinataire ; consultez Tarifs WhatsApp. Un message est facturé en deux étapes, à deux moments différents, et l'objet cost du message indique les deux :
| Champ | Description | Moment d'enregistrement |
|---|---|---|
transaction_amount | Frais de Bird pour le traitement de l'envoi | Lorsque Bird traite l'envoi accepté, avant l'expédition |
passthrough_amount | Part de Meta dans le prix du message, que Bird refacture telle quelle | À l'arrivée d'un accusé delivered ou read applicable |
amount | Somme des composantes déjà tarifées | Augmente à mesure que chaque composante est enregistrée |
currency_code | Devise du portefeuille de votre organisation, commune aux deux composantes | Avec la première composante |
Les deux montants sont des chaînes décimales, hors taxes.
Remarque : un message de service ne comporte aucune part Meta jusqu’au 30 septembre 2026, et il en va de même pour un modèle utilitaire distribué dans une fenêtre de service client ouverte. À partir du 1er octobre 2026, Meta facture les deux, sauf dans une fenêtre de point d’entrée gratuit de 72 heures, avec 1 000 messages de service gratuits par numéro de téléphone professionnel et par mois : modifications tarifaires d’octobre 2026.
Les deux composantes sont tarifées à partir de données différentes. Les frais de Bird reposent sur la catégorie du modèle envoyé et sur le pays du destinataire. Ce pays provient de l’indicatif du numéro de téléphone ou, pour un envoi adressé à un identifiant utilisateur propre à l’entreprise, du préfixe à deux lettres de cet identifiant. La part de Meta repose sur la catégorie que Meta indique lui-même dans l’accusé de réception applicable, qui peut différer de celle du modèle : Meta peut indiquer authentication-international lorsque ses règles de destination, de localisation de l’entreprise et d’éligibilité s’appliquent. Consultez les tarifs authentication-international de WhatsApp.
La valeur lue par cost dépend de l’avancement du message :
- Au moment de la réponse
202,costest null. Rien n’a encore été tarifé. - Après le traitement,
transaction_amountest renseigné etamountlui est égal.passthrough_amountreste null. - Après un accusé de réception
deliveredoureadapplicable, un frais Meta correctement enregistré renseignepassthrough_amount, etamountreflète les composantes enregistrées.
Une composante null signifie qu’aucun montant n’est enregistré dans cette projection ; cela ne prouve pas que le message était gratuit. Une composante explicitement tarifée à zéro affiche "0.00000".
Les deux frais échouent aussi de manière différente. Les frais de Bird échouent en mode fermé : s’ils ne peuvent pas être prélevés après la réponse 202, parce que le portefeuille ne couvre pas l’envoi ou que la route n’a pas de prix configuré, le message se termine en rejected avec le code d’erreur insufficient_balance ou price_not_found, et rien n’est facturé. Un message rejected n’a jamais atteint WhatsApp, ce qui le distingue de failed. La part de Meta échoue en mode ouvert : si le solde du portefeuille est insuffisant ou si le tarif manque à l’arrivée de l’accusé de réception, le frais est ignoré sans annuler l’état observé du message. Le second frais ne retarde jamais la distribution.
Un message facturé par Bird conserve ces frais sortants même si la distribution échoue ensuite. Les frais Meta sont traités à partir d’un callback delivered ou read applicable, lorsque Meta indique une tarification standard avec une catégorie et une destination identifiables. Les deux chemins de callback utilisent la même identité de frais et s’appuient sur la déduplication du service de facturation. Rapprochez les accusés de réception rejoués des enregistrements de facturation, au lieu de considérer la projection du message comme un justificatif de débit définitif. Une tarification de service ou de point d’entrée gratuit peut ramener la composante Meta à zéro ; une composante non résolue ne prouve pas que le message était gratuit.
Utilisez le registre de facturation pour le rapprochement financier. Les champs cost du message sont des projections des frais et peuvent être en retard ou incomplets. Consultez les métriques WhatsApp pour la différence entre les observations de messages et les enregistrements de facturation.
Les événements WhatsApp ne contiennent pas le coût. Pour lire l’une ou l’autre composante, relisez le message avec GET /v1/whatsapp/messages/{id}.
Étapes suivantes
- Messages de service : envoyez du texte, des médias et du contenu interactif pendant la fenêtre de service client
- Modèles : choisissez un modèle et renseignez ses variables
- Recevoir des messages WhatsApp : lisez les réponses et récupérez les médias entrants
- Webhooks de statut des messages : recevez les mises à jour de distribution et d'échec
- Idempotence : réessayez en toute sécurité avec l'en-tête
Idempotency-Key
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet.