Templates define reusable message content. Create and manage them in the dashboard (/templates), via the REST API (/v1/templates), or through the MCP server (templates_create / templates_update / templates_delete). When sending, reference the template by templateId in POST /v1/messages/send and pass a variables object with matching keys.
Categories
The category is set at creation time and cannot be changed later:
- MARKETING — promotional messages (most sensitive to WhatsApp policies).
- UTILITY — transactional messages (account updates, alerts, reminders).
- OTP — one-time / verification code messages.
- Official (Meta) numbers: when you save a template with a Meta number selected, the dashboard asks whether to submit it to Meta for review now or keep it as a draft and submit later. Once submitted, category, language, and approval status follow Meta’s rules. A template is only sendable on a Meta number after it has an approved version.
- Unofficial numbers (Evolution): templates are created locally and are immediately usable — there is no approval step.
Templates returned by GET /v1/templates carry a source field (where the template came from), a metaStatus field (Meta review status, e.g. PENDING, APPROVED, REJECTED), and a sendable flag indicating whether it can be used for sends right now.
Template structure
Only body is required.
| Component | Required | What it is |
|---|
| Header | optional | Text (≤60 chars, ≤1 variable) or one media item (IMAGE JPG/PNG, VIDEO MP4, DOCUMENT PDF). |
| Body | required | Main text (≤1024 chars) with {{variable}} placeholders. |
| Footer | optional | Static text ≤60 chars, no variables. |
| Buttons | optional | Up to 10 buttons: QUICK_REPLY (≤10), URL (≤2), PHONE_NUMBER (≤1, E.164 with country code), COPY_CODE (≤1, alphanumeric ≤15 chars). |
Media header values: via REST pass header.url (public http(s) URL) or header.base64 (data URI, re-hosted server-side automatically). Via MCP only header.url is accepted.
There is no LOCATION header. Also not supported: FLOW, CAROUSEL, CATALOG/MPM, LIMITED_TIME_OFFER, OTP autofill/one-tap/zero-tap, VOICE_CALL buttons, and the NAMED parameter_format.
Variable rules
- Write variables as
{{name}} in the body, text header, and dynamic URL buttons (e.g. https://shop.com/promo/{{link}} — one variable, at the end of the URL, never in the domain).
- The body must not start or end with a variable — always include literal text at both ends.
- When creating/updating via API or MCP, an
examples object mapping every variable to a real sample value is required. Missing samples return 400 TEMPLATE_EXAMPLES_REQUIRED with the missing keys in details.missing.
- The copy-code value is the one exception: it goes on the button as
example (e.g. ["PROMO10"]), not in top-level examples.
- When sending, every placeholder — including keys used only in buttons — must be supplied in
variables, and each value must be a non-empty string.
Text formatting
WhatsApp formatting markers work in the body: *bold*, _italic_, ~strikethrough~, monospace. The text header does not allow formatting markers, emojis, or line breaks.
Templates API
curl -X POST "https://pilotstatus.com.br/v1/templates" \
-H "x-api-key: ps_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "promo_coupon",
"category": "MARKETING",
"language": "pt_BR",
"body": {
"header": { "type": "IMAGE", "url": "https://cdn.example.com/banner.png" },
"body": { "text": "Hi {{name}}! Use it and get {{discount}} off." },
"footer": { "text": "Limited-time offer" },
"buttons": [
{ "type": "QUICK_REPLY", "text": "I want it!" },
{ "type": "URL", "text": "Buy", "url": "https://shop.com/promo/{{link}}" },
{ "type": "COPY_CODE", "example": ["PROMO10"] }
]
},
"examples": { "name": "Maria", "discount": "10%", "link": "abc123" }
}'
A successful create returns 201:
{ "id": "tmpl_123", "name": "promo_coupon", "category": "MARKETING", "metaStatus": "PENDING" }
After publishing a version, GET /v1/templates/{id} returns all detected variable keys in latestVersion.variables.
Common submission and send errors
| Situation | Fix | Meta code |
|---|
| Body starts/ends with a variable | Add literal text at both ends | 2388299 |
| URL button on a placeholder domain (example.com, yourdomain.com…) | Use your real business domain | 368/1346003 |
| Call button phone without country code | Use E.164, e.g. +5521999998888 | 192 |
| Too many variables relative to fixed text | Add wording or drop variables | 2388293 |
| Sending a template not yet approved by Meta | Wait for approval | 131053 |
| Variables sent don’t match the approved template | Send every expected key | 132000 |
| Template paused by Meta for quality drop | Improve content, reduce reports | 132015 |
A 404 when sending means the template does not exist for the key or — on a Meta number — it has no approved version yet.