# Templates

> Submit templates for Meta review, track their status, sync them, and use them in your sends.

A **template** is a message model pre-approved by Meta. It is the only way to message someone who hasn't written to you in the last 24 hours. Templates belong to a **WhatsApp Business account** (WABA): all the numbers in that account share the same templates.

## The lifecycle

```text
You submit ──► pending ──► approved   (usable)
                      └──► rejected   (reason in rejectionReason)
                           paused / disabled  (insufficient quality: decided by Meta)
```

The `status` is Meta's, in lowercase. **Only `approved` can be sent** (`usable: true`).

## Create a template

<ApiExample method="POST" path="/waba-connections/{wabaConnectionId}/templates" />

| Rule | Detail |
|---|---|
| `name` | Lowercase letters, digits, and underscores: `appointment_reminder`. |
| `language` | `fr`, `en`, `en_US`… The same name can exist in several languages. |
| `category` | `MARKETING`, `UTILITY`, or `AUTHENTICATION`. Meta decides which category applies, and the pricing that follows from it. |
| `components` | In Meta's format. The `BODY` component is required; `HEADER`, `FOOTER`, and `BUTTONS` are optional. Variables are written with numbered double curly braces, and the `example` field provides a sample value for each variable. |

The API validates the **structure** of the draft (name, language, category, components, buttons) **before** calling Meta, and responds with `400 validation_error` and details if something is wrong. Meta then applies its own content rules: a rejection arrives later, as a `rejected` status.

## Components

Each component is an object in `components`. The limits below are the ones Meta announces; Meta has the final say.

| Component | Content | Limits |
|---|---|---|
| `HEADER` | `format`: `TEXT`, `IMAGE`, `VIDEO`, `DOCUMENT`, or `LOCATION`. | Text: 60 characters, at most one variable (`example.header_text`). Media: `example.header_handle` with a sample file. |
| `BODY` | `text` with variables `{{1}}`, `{{2}}`… | 1,024 characters. Consecutive numbers starting at 1, and **one example per variable** (`example.body_text`). |
| `FOOTER` | `text`. | 60 characters, no variables. |
| `BUTTONS` | A `buttons` list. | At most 10 buttons. Text of 25 characters. |

Button types:

| `type` | Fields | Rules |
|---|---|---|
| `QUICK_REPLY` | `text` | Quick replies form a single block, at the start or at the end of the list. |
| `URL` | `text`, `url`, `example` | At most 2. `https://` is required. One `{{1}}` variable is allowed, **at the end** of the URL, with a full-URL example. |
| `PHONE_NUMBER` | `text`, `phone_number` | At most 1, with a country code (`+237…`). |
| `COPY_CODE` | `example` | At most 1. Example of 1 to 15 letters or digits. |

```json title="A template with an image header, footer, and buttons"
{
  "name": "christmas_promo",
  "language": "en",
  "category": "MARKETING",
  "components": [
    { "type": "HEADER", "format": "IMAGE", "example": { "header_handle": ["https://example.com/poster.jpg"] } },
    { "type": "BODY", "text": "Hello {{1}}, enjoy {{2}} storewide!", "example": { "body_text": [["Awa", "20% off"]] } },
    { "type": "FOOTER", "text": "Offer valid until 12/31" },
    { "type": "BUTTONS", "buttons": [
      { "type": "QUICK_REPLY", "text": "Interested" },
      { "type": "URL", "text": "See the offer", "url": "https://example.com/offer/{{1}}", "example": ["https://example.com/offer/42"] },
      { "type": "COPY_CODE", "example": "XMAS25" }
    ] }
  ]
}
```

<Callout type="warning" title="Not validated with Meta">
The sample file of a media header is passed as-is in `example.header_handle`. Meta normally expects a handle obtained through its upload API: accepting it as an `https://` URL has not been verified with a real Meta account.
</Callout>

### Authentication templates

For `AUTHENTICATION`, Meta provides the message text itself: no free text, no header. The `BODY` only carries `add_security_recommendation`, the `FOOTER` only `code_expiration_minutes` (1 to 90), and an `OTP` button is required:

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

## Track the review

You have two options:

- **Webhooks**: the `template.status_updated` event notifies you as soon as Meta rules on a template.
- **Sync**, a repair path rather than a polling loop:

<ApiExample method="POST" path="/waba-connections/{wabaConnectionId}/templates/sync" />

Sync aligns the database with Meta: statuses are updated, and templates created elsewhere (directly with Meta, for example) are added. Status changes also trigger `template.status_updated`.

<ApiExample method="GET" path="/waba-connections/{wabaConnectionId}/templates" />

## Send a template

Reference it by `name` and `language`. Variables are passed in `components`, in the order of their numbers:

```json title="POST /messages body"
{
  "phoneNumberId": "…",
  "to": "+237600000000",
  "type": "template",
  "template": {
    "name": "appointment_reminder",
    "language": "en",
    "components": [
      { "type": "body", "parameters": [
        { "type": "text", "text": "Awa" },
        { "type": "text", "text": "November 14 at 10 am" }
      ] }
    ]
  }
}
```

If the template exists in Bref Point Inbox but is **not approved**, the send is rejected immediately, with the status and reason:

```json title="422 template_not_approved"
{ "error": { "code": "template_not_approved",
  "message": "Template “appointment_reminder” (en) is “rejected”: INVALID_FORMAT. Only an approved template can be sent" } }
```

A template **unknown** to Bref Point Inbox (created directly with Meta and not yet synced) is not blocked upfront: Meta decides, and the message goes to `failed` if the template doesn't exist.

## Delete

<ApiExample method="DELETE" path="/waba-connections/{wabaConnectionId}/templates/{name}" />

Deletion removes the template **in all its languages**, first with Meta, then in Bref Point Inbox.

<Callout type="note" title="Authentication templates (OTP)">
The `AUTHENTICATION` category is accepted at creation. The components specific to one-time codes follow Meta's format.
</Callout>
