Skip to main content
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.

Meta approval vs. local templates

  • 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.
ComponentRequiredWhat it is
HeaderoptionalText (≤60 chars, ≤1 variable) or one media item (IMAGE JPG/PNG, VIDEO MP4, DOCUMENT PDF).
BodyrequiredMain text (≤1024 chars) with {{variable}} placeholders.
FooteroptionalStatic text ≤60 chars, no variables.
ButtonsoptionalUp 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

SituationFixMeta code
Body starts/ends with a variableAdd literal text at both ends2388299
URL button on a placeholder domain (example.com, yourdomain.com…)Use your real business domain368/1346003
Call button phone without country codeUse E.164, e.g. +5521999998888192
Too many variables relative to fixed textAdd wording or drop variables2388293
Sending a template not yet approved by MetaWait for approval131053
Variables sent don’t match the approved templateSend every expected key132000
Template paused by Meta for quality dropImprove content, reduce reports132015
A 404 when sending means the template does not exist for the key or — on a Meta number — it has no approved version yet.