Templates
Templates are pre-approved messages. WhatsApp requires one to open a conversation, and to reply outside the 24-hour window, which in practice means almost every notification your system sends.
Creating a template starts a Meta review that can take hours, so it is done in the dashboard, where its status is visible. This API reads them.
GET /v1/templates
Section titled “GET /v1/templates”Scope: templates:read
| Query | Notes |
|---|---|
status | APPROVED, PENDING or REJECTED. Case insensitive. |
account | Restrict to one WhatsApp number, by name |
search | Matches the template name |
limit, cursor | Standard pagination |
{ "data": [ { "id": "7c6b5a4d-...", "name": "order_shipped", "language": "ar", "category": "UTILITY", "status": "APPROVED", "account": "Main", "header_type": "TEXT", "header_content": "Order {{order}}", "body": "Hello {{name}}, your order is on its way. Track it with {{tracking}}.", "footer": "Zaiti Auto Parts", "parameters": ["name", "tracking"], "header_parameters": ["order"], "buttons": [], "quality_rating": "GREEN", "created_at": "2026-06-11T08:00:00Z", "updated_at": "2026-06-12T10:30:00Z" } ], "has_more": false}parameters and header_parameters
Section titled “parameters and header_parameters”The variables the template declares, in the order WhatsApp expects them.
They are listed separately because Meta indexes variables per component:
header {{1}} and body {{1}} are different values. Combining them into one
list would invite you to collapse them, and the header would receive the wrong
text.
Both are always arrays. A template with no variables reports [], never
null, so you can iterate without a guard.
Only APPROVED templates can be sent. Sending any other status is refused with
template_not_approved, naming the template and its current state.
GET /v1/templates/{id}
Section titled “GET /v1/templates/{id}”Scope: templates:read
Same shape, one template.
Using them
Section titled “Using them”// Read what the template needs, rather than assuming.const { data: [template] } = await get('/v1/templates?search=order_shipped&status=approved')
const parameters = Object.fromEntries( template.parameters.map(name => [name, values[name]]))
await post('/v1/messages', { to: customer.phone, type: 'template', template: { name: template.name, parameters },})Building the parameter map from template.parameters rather than hard-coding
it means a template edited in the dashboard does not silently start sending
messages with gaps in them.