Skip to content

Templates

WhatsApp templates are pre-approved messages you can send at any time. Meta reviews every new template, so a new template is PENDING until Meta marks it APPROVED or REJECTED. Every endpoint on this page needs the /templates grant.

GET /v2/templates

Query Default Description
status all APPROVED, PENDING or REJECTED
search Filter by name
page 1 Page number, starting at 1
pageSize 50 Up to 100
Terminal window
curl "https://api.wabot.shop/v2/templates?status=APPROVED&pageSize=20" -H "Authorization: $TOKEN"
{
"data": {
"templates": [
{ "template_name": "order_update", "category": "UTILITY", "language": "en", "status": "APPROVED", "variables": ["name", "custom_order"] }
],
"page": 1,
"pageSize": 20,
"total": 14
}
}

variables lists the keys to pass when you send the template.

The older GET /templates/get-templates endpoint returns every approved template in one response, with no pagination.

GET /v2/templates/{name}

Returns {"data": {…template, "variables": […]}}, or 404 {"message": "Template not found"}.

POST /templates/create

Field Required Description
template_name Yes Lowercase letters, numbers and underscores, unique in your account, e.g. diwali_offer_2026
category Yes MARKETING, UTILITY or AUTHENTICATION
language Yes Language code, e.g. en or en_US
whatsappPhoneId Yes The number the template belongs to
components Yes Header, body, footer, buttons or carousel (see below)
categoryChangeAllowed No Lets Meta change the category if it doesn’t match the content

Write the body text with named variables in double braces, and give an example value for each:

{
"template_name": "order_update",
"category": "UTILITY",
"language": "en",
"whatsappPhoneId": "123456789012345",
"components": [
{ "type": "header", "parameters": [{ "type": "text", "text": "Order update" }] },
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Hi {{name}}, your order {{order}} has shipped." },
{ "type": "variable", "variable": { "text": "name", "value": "Asha" } },
{ "type": "variable", "variable": { "text": "order", "value": "#1042" } }
]
},
{ "type": "footer", "parameters": [{ "type": "text", "text": "Reply STOP to opt out" }] },
{
"type": "button",
"parameters": [
{ "type": "cta", "cta": { "text": "URL", "value": "Track order", "url": "https://example.com/track" } },
{ "type": "quick_reply", "quick_reply": { "value": "Talk to us" } }
]
}
]
}
Component Shape
Text header {"type": "header", "parameters": [{"type": "text", "text": "…"}]}
Media header {"type": "image", "image": {"link": "https://…"}}. Also video or document
Body {"type": "body", "parameters": [text, …variables]}
Footer {"type": "footer", "parameters": [{"type": "text", "text": "…"}]}
Quick reply button {"type": "quick_reply", "quick_reply": {"value": "Yes"}}
URL button {"type": "cta", "cta": {"text": "URL", "value": "Label", "url": "https://…"}}
Call button {"type": "cta", "cta": {"text": "Phone", "value": "Call us", "url": "+91…"}}
Carousel {"type": "carousel", "components": [{header, body, buttons}, …]}

Response 200: {"data": {…template, "status": "PENDING"}}.

If the name is already taken, or the phone ID isn’t found, the response is still 200, but data is a message string such as "The template \"order_update\" is already exist". Check whether data is a string or an object.

GET /templates/status?template_name=order_update

{ "data": { "status": "APPROVED" } }

To pull the latest status from Meta for all templates at once:

POST /v2/templates/sync

{
"data": {
"updated": [{ "name": "order_update", "status": "REJECTED", "rejected_reason": "INVALID_FORMAT" }],
"errors": []
}
}

You can also subscribe to the template_status_update webhook instead of polling.

PUT /templates/update?template_name=order_update

Body: {"components": […], "category": "UTILITY"}. Meta reviews the template again after an update.

  1. POST /templates/create creates the template, which starts as PENDING.
  2. Call GET /templates/status every few minutes, or wait for the template_status_update webhook.
  3. Once the status is APPROVED, send it.