Skip to main content

Templates API

A template is a reusable message body you author once and send many times — a WhatsApp/RCS template that needs carrier approval before it can be used, an SMS or email body, or an interactive layout. Templates carry their own scope (templates:read / templates:write) distinct from messages:*, because managing a template is a content-authoring + carrier-approval action, not just a send. Base path: /api/v1/messages/templates Authentication: API key (X-API-Key) or session JWT. GET /api/v1/templates is a read-only search alias at the top level (used by the dashboard’s command-palette search) that delegates to the same list handler and scope — writes always go through /messages/templates.

Creating a template

For channel: "whatsapp" with a components array (the rich HEADER / BODY / FOOTER / BUTTONS shape), the template is submitted for carrier approval using your own WhatsApp Business Account credentials rather than platform-shared ones. Other channels (SMS, email, RCS, etc.) use a plain content string.
A newly-created WhatsApp/RCS template starts in a pending carrier-approval status; poll GET /{id} or listen for the template-approval webhook to know when it’s ready to send. The response carries the template id and its initial status:
A WhatsApp template can only be sent once it reaches approved. For non-carrier-approval channels (SMS, email) the template is usable immediately.

Sending a template message

Reference the approved template by name on the unified send endpoint and pass the positional variable substitutions in variables (keyed by the {{1}}, {{2}} placeholders you authored):
Either body, template_name, or media_url is required on every send — a template send substitutes for the freeform body. See the Messaging API reference for the full request and response shape.

AI localization

POST /{id}/localize auto-translates one canonical template’s body and button labels into up to 25 target locales, then creates one (channel, name, language) row per locale through the same carrier-submission path a hand-authored variant uses — so each translated variant goes through carrier approval exactly like an original. GET /{id}/localizations returns the per-locale approval tracker: every language variant sharing the template’s (channel, name), each with its own status.

Reusable content templates

/api/v1/messages/content-templates is a separate, channel-agnostic layer above per-channel templates — group several channel-specific variants under one logical content id with an author-declared fallback chain (send WhatsApp if approved, else fall back to SMS), similar to Twilio’s Content API.

Cross-campaign analytics

GET /api/v1/templates/{id}/analytics returns the consolidated engagement rollup for a template reused across many campaigns — sent / delivered / read / click counts aggregated across every campaign that used it, rather than the per-campaign view alone.
The response contains four groups of fields:
  • template — the template’s id, name, channel, and current approval status. A 404 here means the id does not exist in your tenant.
  • totals — engagement counters aggregated across every campaign that referenced the template: sent, delivered, failed, opened, clicked.
  • rates — the derived funnel: delivery_rate (delivered over terminal messages), open_rate (opened over delivered), and click_rate (clicked over delivered). Rates are fractions rounded to two decimals, e.g. 0.42 for 42%.
  • campaigns — the per-campaign breakdown, one entry per campaign that used the template with its own counters and status, so a strong- or weak-performing campaign is visible against the rollup.
A degraded: true flag on the response means the live per-message scan timed out and the numbers were read from denormalized campaign counters instead — the totals remain correct for campaigns that have run, but very recent sends may not be reflected yet.

See also