> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchbord.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Template rules reference

> Every WhatsApp template validator rule keyed to Meta's rejection reasons — with DO and DON'T examples for each.

This page is the authoritative reference for every structural rule Switchbord checks before submitting a template to Meta. Each section is keyed to the error code the client-side validator emits (see BORD-335) so you can jump straight from a red editor banner to the fix.

All rules are enforced by both the Switchbord client-side linter and Meta's submit-time validator. Meta's human reviewer adds a second layer of content checks on top (see [Marketing templates](/whatsapp/marketing-templates)).

## name-regex

<a id="name-regex" />

Template names are used as stable identifiers across Meta, webhooks, and our database. Meta enforces lowercase-only characters so names are URL-safe and diff-friendly across BSPs.

**Rule:** `^[a-z][a-z0-9_]{0,511}$` — lowercase letters, digits, and underscores only. No spaces, hyphens, uppercase, dots, or accented characters. Must be unique per WABA per language.

**DO**

```text theme={null}
booking_confirmed
invoice_ready
winback_30d
```

**DON'T**

```text theme={null}
Booking Confirmed      // spaces and uppercase
invoice-ready          // hyphen
booking.v2             // dot
prenotazioneConfermata // camelCase
```

Meta docs: [Template guidelines — naming](https://developers.facebook.com/docs/whatsapp/message-templates/guidelines).

## leading-variable

<a id="leading-variable" />

Meta rejects body text that starts with a placeholder because it renders awkwardly on mobile and gives the model no anchor for category classification. A body that begins with a name or greeting reads like a message; one that begins with `{{1}}` reads like a template leak.

**DO**

```text theme={null}
Ciao {{1}}, la tua prenotazione è confermata.
```

**DON'T**

```text theme={null}
{{1}} è confermata.
```

Meta docs: [Message template guidelines](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates).

## trailing-variable

<a id="trailing-variable" />

The mirror of the leading-variable rule. A body that ends on `{{N}}` — even with a trailing full stop — is rejected. Add at least one fixed word after the last placeholder, ideally a full sentence of context.

**DO**

```text theme={null}
Rimborso di {{1}} EUR accreditato entro 7 giorni.
```

**DON'T**

```text theme={null}
Rimborso di {{1}}
```

Meta docs: [Message template guidelines](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates).

## adjacent-variables

<a id="adjacent-variables" />

Two placeholders with only whitespace or punctuation between them are rejected. Meta cannot render the boundary cleanly and cannot classify the intent when the body is effectively a parameter list. Put at least one real word between every pair.

**DO**

```text theme={null}
Ciao {{1}}, il tuo codice ordine è {{2}}.
```

**DON'T**

```text theme={null}
{{1}}, {{2}}
{{1}} {{2}}
{{1}}-{{2}}
```

Meta docs: [Message template guidelines](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates).

## missing-example

<a id="missing-example" />

Every `{{n}}` in a `HEADER`, `BODY`, or dynamic URL button must have a matching realistic example. Meta uses examples to render the preview shown to reviewers and to classify the template's category. A template with placeholders and no examples is rejected at submit time.

**DO**

```json theme={null}
{
  "type": "BODY",
  "text": "Hi {{1}}, your order {{2}} ships today.",
  "example": { "body_text": [["Marco Rossi", "GBV-2025-0042"]] }
}
```

**DON'T**

```json theme={null}
{
  "type": "BODY",
  "text": "Hi {{1}}, your order {{2}} ships today."
}
```

Meta docs: [Create and submit a template](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates).

## example-count-mismatch

<a id="example-count-mismatch" />

The length of the example array must equal the number of distinct placeholders. Three `{{n}}` tokens require exactly three example strings — not two, not four. This check runs per component (body, header text, URL button).

**DO**

```json theme={null}
"text": "Hi {{1}}, order {{2}} — total {{3}} EUR.",
"example": { "body_text": [["Marco", "GBV-0042", "450,00"]] }
```

**DON'T**

```json theme={null}
"text": "Hi {{1}}, order {{2}} — total {{3}} EUR.",
"example": { "body_text": [["Marco", "GBV-0042"]] }   // only 2 examples for 3 vars
```

Meta docs: [Template parameters](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates#examples).

## numeric-only-example

<a id="numeric-only-example" />

Example values must be realistic — that is, they should contain alphabetic characters or a clearly meaningful alphanumeric token (currency amount with symbol, a date string, an order code). A bare number, a single symbol, or a code-like string with no context is rejected because it does not represent how a real recipient will see the message.

**DO**

```json theme={null}
"example": { "body_text": [["20%", "Parigi", "15 maggio 2025", "FLASH20"]] }
```

**DON'T**

```json theme={null}
"example": { "body_text": [["20", "5", "2025", "20"]] }   // bare numbers
"example": { "body_text": [["%", "€", "—", "."]] }        // symbols only
```

Bake the unit into the example — write `"20%"`, not `"20"` with the `%` sitting adjacent in the body. That removes the "bare number" heuristic and renders identically.

Meta docs: [Example values](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates#examples).

## sequential-numbering

<a id="sequential-numbering" />

Placeholders must start at `{{1}}` and run sequentially with no gaps and no duplicates. `{{1}}, {{2}}, {{3}}` is valid; `{{1}}, {{3}}` is not; `{{2}}, {{3}}` is not (must start at 1). The validator extracts the unique indices, sorts them, and checks the resulting list equals `[1..N]`.

**DO**

```text theme={null}
Hi {{1}}, order {{2}} ships {{3}}.
```

**DON'T**

```text theme={null}
Hi {{1}}, order {{3}}.        // missing {{2}}
Hi {{2}}, welcome.            // doesn't start at 1
Hi {{1}}, {{1}} again.        // duplicate index
```

Meta docs: [Template parameters](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates).

## params-words-ratio

<a id="params-words-ratio" />

A template where placeholders dominate the fixed text reads like a raw data dump and is flagged during content review. The Switchbord linter warns when the ratio of variables to total words exceeds roughly **1 in 4** — a template with three `{{n}}` tokens should have at least twelve words of fixed copy around them.

**DO**

```text theme={null}
Ciao {{1}}, la tua prenotazione per {{2}} è confermata. Il codice viaggio è {{3}}, conservalo per il check-in.
```

(12 fixed words around 3 placeholders)

**DON'T**

```text theme={null}
{{1}}: {{2}} — {{3}}
```

(3 placeholders, 2 fixed tokens)

Meta docs: [Template quality](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/guidelines).

## length-caps

<a id="length-caps" />

Each component has a hard character limit. Count the raw template string including placeholders — the runtime expansion does not count against the limit.

| Component    | Limit          |
| ------------ | -------------- |
| HEADER text  | **60** chars   |
| BODY text    | **1024** chars |
| FOOTER text  | **60** chars   |
| Button label | **25** chars   |

**DO**

```text theme={null}
HEADER: "Estate 2025 — offerte"                (22 chars)
FOOTER: "Rispondi STOP per non ricevere più."   (35 chars)
Button: "Scarica PDF"                           (11 chars)
```

**DON'T**

```text theme={null}
HEADER: "Offerta esclusiva di fine stagione ..."   (> 60 chars)
Button: "Scopri tutte le offerte di questo mese"   (> 25 chars)
```

Meta docs: [Template components](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates#components).

## button-limits

<a id="button-limits" />

Buttons have per-type hard caps. Exceeding any one cap rejects the whole template at submit.

| Button type    | Max                          |
| -------------- | ---------------------------- |
| `QUICK_REPLY`  | **3** per template           |
| `URL`          | **2** (max 1 dynamic suffix) |
| `PHONE_NUMBER` | **1**                        |
| `COPY_CODE`    | **1**                        |
| `FLOW`         | **1**                        |
| **Total**      | **10** across all types      |

Dynamic URL buttons must have `{{1}}` as the only variable, appearing **at the end** of the URL path or query value — never in the hostname.

**DO**

```json theme={null}
"buttons": [
  { "type": "QUICK_REPLY", "text": "Mare" },
  { "type": "QUICK_REPLY", "text": "Montagna" },
  { "type": "QUICK_REPLY", "text": "Città d'arte" },
  { "type": "URL", "text": "Scarica PDF",
    "url": "https://gbviaggi.it/fattura/{{1}}",
    "example": ["https://gbviaggi.it/fattura/2025-0142"] }
]
```

**DON'T**

```json theme={null}
"buttons": [
  { "type": "QUICK_REPLY", "text": "A" },
  { "type": "QUICK_REPLY", "text": "B" },
  { "type": "QUICK_REPLY", "text": "C" },
  { "type": "QUICK_REPLY", "text": "D" }   // 4 > max 3
]

"url": "https://{{1}}.example.com"          // variable in hostname
"url": "https://example.com/{{1}}/{{2}}"   // more than one variable
```

Meta docs: [Template buttons](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates#buttons).

## component-ordering

<a id="component-ordering" />

A template's components must appear in the order `HEADER → BODY → FOOTER → BUTTONS`. Each component may appear at most once, except `BODY` which is required exactly once. Meta normalises the order in some cases but rejects duplicates and unknown types with code 100.

**DO**

```json theme={null}
"components": [
  { "type": "HEADER", "format": "TEXT", "text": "Estate 2025" },
  { "type": "BODY", "text": "Ciao {{1}}, ..." },
  { "type": "FOOTER", "text": "Rispondi STOP." },
  { "type": "BUTTONS", "buttons": [ ... ] }
]
```

**DON'T**

```json theme={null}
"components": [
  { "type": "BODY", "text": "..." },
  { "type": "HEADER", "format": "TEXT", "text": "..." },   // wrong position
  { "type": "BODY", "text": "..." }                         // duplicate
]
```

Meta docs: [Template components](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates#components).

## footer-has-variables

<a id="footer-has-variables" />

Footers are for static, legally-important copy — the sender identity, the opt-out instruction, a disclosure. They cannot contain `{{n}}` placeholders. Put runtime data in the body.

**DO**

```json theme={null}
{ "type": "FOOTER", "text": "GB Viaggi — Rispondi STOP per non ricevere più offerte." }
```

**DON'T**

```json theme={null}
{ "type": "FOOTER", "text": "Sent to {{1}} on {{2}}" }
```

Meta docs: [Template components — footer](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/creating-templates#components).

***

## Quick-reference regex set

If you are writing your own validator or scripting bulk checks, the canonical regexes are:

```js theme={null}
const PLACEHOLDER       = /\{\{(\d+)\}\}/g;
const adjacentVars      = /\{\{\d+\}\}[^A-Za-zÀ-ÿ0-9]{0,3}\{\{\d+\}\}/;
const startsWithVar     = /^\s*\{\{\d+\}\}/;
const endsWithVar       = /\{\{\d+\}\}\s*[.!?]*\s*$/;
const bareNumberExample = /^[\d\s.,%$€£¥+\-]+$/;
const nameRule          = /^[a-z][a-z0-9_]{0,511}$/;
const langRule          = /^[a-z]{2}(_[A-Z]{2})?$/;
const dynamicUrl        = /^https?:\/\/[^{}\s]+?(\{\{1\}\})?$/;
```

## See also

* [Your first template](/getting-started/first-template) — end-to-end walkthrough.
* [Marketing templates](/whatsapp/marketing-templates) — category-specific rules and opt-out.
* [Template API endpoints](/api-reference/template-endpoints) — programmatic submission.
