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#

texte
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#

bash
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"
            ]
          ]
        }
      }
    ]
  }'
Réponse · 201 Created
{
  "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ègleDétail
nameMinuscules, chiffres et underscores : rappel_rdv.
languagefr, en, en_US… Un même nom peut exister en plusieurs langues.
categoryMARKETING, UTILITY ou AUTHENTICATION. Meta décide de la catégorie retenue et de la tarification qui en découle.
componentsAu 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 la structure du brouillon (nom, langue, catégorie, composants, boutons) 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.

Les composants#

Chaque composant est un objet de components. Les limites ci-dessous sont celles que Meta annonce ; Meta reste seul juge.

ComposantContenuLimites
HEADERformat : TEXT, IMAGE, VIDEO, DOCUMENT ou LOCATION.Texte : 60 caractères, une variable au plus (example.header_text). Média : example.header_handle avec un exemple de fichier.
BODYtext avec variables {{1}}, {{2}}…1 024 caractères. Numéros consécutifs depuis 1, et un exemple par variable (example.body_text).
FOOTERtext.60 caractères, sans variable.
BUTTONSListe buttons.10 boutons au plus. Texte de 25 caractères.

Types de bouton :

typeChampsRègles
QUICK_REPLYtextLes réponses rapides forment un seul bloc, au début ou à la fin de la liste.
URLtext, url, example2 au plus. https:// obligatoire. Une variable {{1}} possible, à la fin de l'adresse, avec un exemple d'adresse complète.
PHONE_NUMBERtext, phone_number1 au plus, avec indicatif pays (+237…).
COPY_CODEexample1 au plus. Exemple de 1 à 15 lettres ou chiffres.
Un template avec en-tête image, pied de page et boutons
{
  "name": "promo_noel",
  "language": "fr",
  "category": "MARKETING",
  "components": [
    { "type": "HEADER", "format": "IMAGE", "example": { "header_handle": ["https://exemple.com/affiche.jpg"] } },
    { "type": "BODY", "text": "Bonjour {{1}}, profitez de {{2}} sur tout le magasin !", "example": { "body_text": [["Awa", "-20 %"]] } },
    { "type": "FOOTER", "text": "Offre valable jusqu'au 31/12" },
    { "type": "BUTTONS", "buttons": [
      { "type": "QUICK_REPLY", "text": "Intéressé" },
      { "type": "URL", "text": "Voir l'offre", "url": "https://exemple.com/offre/{{1}}", "example": ["https://exemple.com/offre/42"] },
      { "type": "COPY_CODE", "example": "NOEL25" }
    ] }
  ]
}

Templates d'authentification#

Pour AUTHENTICATION, Meta fournit lui-même le texte du message : pas de texte libre, pas d'en-tête. Le BODY ne porte que add_security_recommendation, le FOOTER que code_expiration_minutes (1 à 90), et un bouton OTP est obligatoire :

json
[
  { "type": "BODY", "add_security_recommendation": true },
  { "type": "FOOTER", "code_expiration_minutes": 10 },
  { "type": "BUTTONS", "buttons": [{ "type": "OTP", "otp_type": "COPY_CODE", "text": "Copier le code" }] }
]

Suivre la revue#

Vous avez deux moyens :

  • Les webhooks : l'événement template.status_updated vous prévient dès que Meta statue.
  • La synchronisation, un chemin de réparation plus qu'une boucle de scrutation :
bash
curl -X POST "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates/sync" \
  -H "Authorization: Bearer $INBOX_KEY"
Réponse · 200 OK
[
  {
    "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.

bash
curl "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates" \
  -H "Authorization: Bearer $INBOX_KEY"
Réponse · 200 OK
[
  {
    "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 :

Corps de POST /messages
{
  "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 :

422 template_not_approved
{ "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#

bash
curl -X DELETE "$INBOX_API/waba-connections/WABA_CONNECTION_ID/templates/NAME" \
  -H "Authorization: Bearer $INBOX_KEY"
Réponse · 204 No Content
(aucun contenu)

La suppression retire le template dans toutes ses langues, chez Meta puis chez Bref Point Inbox.