Guides
Templates
Soumettre des templates à la revue de Meta, suivre leur statut, les synchroniser et les utiliser dans vos envois.
Mis à jour le 12 novembre 2026Voir en Markdown
Un template est un modèle de message pré-approuvé par Meta. C'est le seul moyen d'écrire à quelqu'un qui ne vous a pas écrit dans les dernières 24 heures. Les templates appartiennent à un compte WhatsApp (WABA) : tous les numéros de ce compte partagent les mêmes templates.
Le cycle de vie#
Vous soumettez ──► pending ──► approved (utilisable)
└──► rejected (motif dans rejectionReason)
paused / disabled (qualité insuffisante : décidé par Meta)Le status est celui de Meta, en minuscules. Seul approved est envoyable (usable: true).
Créer un template#
curl -X POST "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates" \
-H "Authorization: Bearer $INBOX_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "rappel_rdv",
"language": "fr",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.",
"example": {
"body_text": [
[
"Awa",
"14 novembre à 10 h"
]
]
}
}
]
}'const res = await fetch(`${process.env.INBOX_API}/waba-connections/WABA_CONNECTION_ID/templates`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.INBOX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "rappel_rdv",
"language": "fr",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.",
"example": {
"body_text": [
[
"Awa",
"14 novembre à 10 h"
]
]
}
}
]
}),
});
const data = await res.json();import os
import requests
res = requests.post(
f"{os.environ['INBOX_API']}/waba-connections/WABA_CONNECTION_ID/templates",
headers={
"Authorization": f"Bearer {os.environ['INBOX_KEY']}",
},
json={
"name": "rappel_rdv",
"language": "fr",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.",
"example": {
"body_text": [
[
"Awa",
"14 novembre à 10 h",
],
],
},
},
],
},
)
print(res.json())<?php
$ch = curl_init(getenv('INBOX_API') . '/waba-connections/WABA_CONNECTION_ID/templates');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('INBOX_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'rappel_rdv',
'language' => 'fr',
'category' => 'UTILITY',
'components' => [
[
'type' => 'BODY',
'text' => 'Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.',
'example' => [
'body_text' => [
[
'Awa',
'14 novembre à 10 h',
],
],
],
],
],
]),
]);
$data = json_decode(curl_exec($ch), true);{
"id": "ff532b8f-2956-4f04-851f-db09d5eefbd4",
"name": "rappel_rdv",
"language": "fr",
"category": "UTILITY",
"status": "pending",
"usable": false,
"rejectionReason": null,
"components": [
{
"type": "BODY",
"text": "Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.",
"example": {
"body_text": [
[
"Awa",
"14 novembre à 10 h"
]
]
}
}
],
"updatedAt": "2026-11-12T08:33:45.595Z"
}| Règle | Détail |
|---|---|
name | Minuscules, chiffres et underscores : rappel_rdv. |
language | fr, en, en_US… Un même nom peut exister en plusieurs langues. |
category | MARKETING, UTILITY ou AUTHENTICATION. Meta décide de la catégorie retenue et de la tarification qui en découle. |
components | Au format Meta. Le composant BODY est obligatoire ; HEADER, FOOTER et BUTTONS sont facultatifs. Les variables s'écrivent avec des doubles accolades numérotées, et le champ example fournit une valeur d'exemple pour chaque variable. |
L'API valide le brouillon (nom, langue, catégorie, présence du BODY) avant d'appeler Meta et répond 400 validation_error avec le détail si quelque chose ne va pas. Meta applique ensuite ses propres règles de contenu : un rejet arrive plus tard, en statut rejected.
Suivre la revue#
Vous avez deux moyens :
- Les webhooks : l'événement
template.status_updatedvous prévient dès que Meta statue. - La synchronisation, un chemin de réparation plus qu'une boucle de scrutation :
curl -X POST "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates/sync" \
-H "Authorization: Bearer $INBOX_KEY"const res = await fetch(`${process.env.INBOX_API}/waba-connections/WABA_CONNECTION_ID/templates/sync`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.INBOX_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.post(
f"{os.environ['INBOX_API']}/waba-connections/WABA_CONNECTION_ID/templates/sync",
headers={
"Authorization": f"Bearer {os.environ['INBOX_KEY']}",
},
)
print(res.json())<?php
$ch = curl_init(getenv('INBOX_API') . '/waba-connections/WABA_CONNECTION_ID/templates/sync');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('INBOX_KEY'),
],
]);
$data = json_decode(curl_exec($ch), true);[
{
"id": "ff532b8f-2956-4f04-851f-db09d5eefbd4",
"name": "rappel_rdv",
"language": "fr",
"category": "UTILITY",
"status": "approved",
"usable": true,
"rejectionReason": null,
"components": [
{
"text": "Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.",
"type": "BODY"
}
],
"updatedAt": "2026-11-12T08:33:45.640Z"
}
]La synchronisation aligne la base sur Meta : statuts mis à jour, et templates créés ailleurs (directement chez Meta, par exemple) ajoutés. Les changements de statut déclenchent aussi template.status_updated.
curl "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates" \
-H "Authorization: Bearer $INBOX_KEY"const res = await fetch(`${process.env.INBOX_API}/waba-connections/WABA_CONNECTION_ID/templates`, {
headers: {
Authorization: `Bearer ${process.env.INBOX_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
f"{os.environ['INBOX_API']}/waba-connections/WABA_CONNECTION_ID/templates",
headers={
"Authorization": f"Bearer {os.environ['INBOX_KEY']}",
},
)
print(res.json())<?php
$ch = curl_init(getenv('INBOX_API') . '/waba-connections/WABA_CONNECTION_ID/templates');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('INBOX_KEY'),
],
]);
$data = json_decode(curl_exec($ch), true);[
{
"id": "ff532b8f-2956-4f04-851f-db09d5eefbd4",
"name": "rappel_rdv",
"language": "fr",
"category": "UTILITY",
"status": "approved",
"usable": true,
"rejectionReason": null,
"components": [
{
"text": "Bonjour {{1}}, votre rendez-vous est confirmé pour le {{2}}.",
"type": "BODY"
}
],
"updatedAt": "2026-11-12T08:33:45.640Z"
}
]Envoyer un template#
Référencez-le par name et language. Les variables se passent dans components, dans l'ordre de leurs numéros :
{
"phoneNumberId": "…",
"to": "+237600000000",
"type": "template",
"template": {
"name": "rappel_rdv",
"language": "fr",
"components": [
{ "type": "body", "parameters": [
{ "type": "text", "text": "Awa" },
{ "type": "text", "text": "14 novembre à 10 h" }
] }
]
}
}Si le template existe chez Bref Point Inbox mais n'est pas approuvé, l'envoi est refusé immédiatement, avec le statut et le motif :
{ "error": { "code": "template_not_approved",
"message": "Le template « rappel_rdv » (fr) est « rejected » : INVALID_FORMAT. Seul un template approuvé peut être envoyé" } }Un template inconnu de Bref Point Inbox (créé directement chez Meta et pas encore synchronisé) n'est pas bloqué en amont : c'est Meta qui tranche, et le message passe en failed si le template n'existe pas.
Supprimer#
curl -X DELETE "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates/NAME" \
-H "Authorization: Bearer $INBOX_KEY"const res = await fetch(`${process.env.INBOX_API}/waba-connections/WABA_CONNECTION_ID/templates/NAME`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.INBOX_KEY}`,
},
});
const data = res.status;import os
import requests
res = requests.delete(
f"{os.environ['INBOX_API']}/waba-connections/WABA_CONNECTION_ID/templates/NAME",
headers={
"Authorization": f"Bearer {os.environ['INBOX_KEY']}",
},
)
print(res.status_code)<?php
$ch = curl_init(getenv('INBOX_API') . '/waba-connections/WABA_CONNECTION_ID/templates/NAME');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('INBOX_KEY'),
],
]);
curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE);(aucun contenu)La suppression retire le template dans toutes ses langues, chez Meta puis chez Bref Point Inbox.