Skip to content

Templates

WhatsApp Message Templates are pre-approved message formats required for sending business-initiated messages. Templates must be approved by Meta before use.

Retrieve all templates for your account.

Terminal window
GET /api/templates
ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerPage size, 1–100. Default 50.
statusstringFilter by status (APPROVED, PENDING, REJECTED)
categorystringFilter by category (MARKETING, UTILITY, AUTHENTICATION)
accountstringFilter by WhatsApp account name
searchstringCase-insensitive match on name or display_name

Ordered by created_at DESC.

{
"status": "success",
"data": {
"templates": [
{
"id": "uuid",
"whatsapp_account": "Main Business",
"meta_template_id": "123456789",
"name": "order_confirmation",
"display_name": "Order Confirmation",
"language": "en",
"category": "UTILITY",
"status": "APPROVED",
"header_type": "IMAGE",
"header_content": "",
"body_content": "Hi {{1}}, your order #{{2}} has been confirmed!",
"footer_content": "Thank you for shopping with us",
"buttons": [
{ "type": "URL", "text": "Track Order", "url": "https://example.com/track/{{1}}" }
],
"sample_values": [],
"add_security_recommendation": false,
"code_expiration_minutes": 0,
"quality_rating": "UNKNOWN",
"created_by_name": "Jane Admin",
"updated_by_name": "Jane Admin",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"total": 50,
"page": 1,
"limit": 50
}
}

Retrieve a single template by ID. Returns the same object shape as a list item.

Terminal window
GET /api/templates/{id}

Create a new template locally. Creating does not submit it to Meta — call Submit Template for that.

Terminal window
POST /api/templates
{
"whatsapp_account": "Main Business",
"name": "welcome_message",
"display_name": "Welcome Message",
"language": "en",
"category": "MARKETING",
"header_type": "TEXT",
"header_content": "Welcome!",
"body_content": "Welcome to our store, {{1}}! Use code {{2}} for 10% off your first order.",
"footer_content": "Reply STOP to unsubscribe",
"buttons": [],
"sample_values": ["John", "SAVE10"]
}
FieldTypeRequiredDescription
whatsapp_accountstringYesAccount name
namestringYesMeta template name (lowercase, underscores)
languagestringYesLanguage code, e.g. en
categorystringYesMARKETING, UTILITY, or AUTHENTICATION
display_namestringNoFriendly label shown in the UI
header_typestringNoTEXT, IMAGE, DOCUMENT, VIDEO, or NONE
header_contentstringNoHeader text when header_type is TEXT
body_contentstringNoBody text with {{1}}-style or named placeholders
footer_contentstringNoFooter text
buttonsarrayNoButton definitions
sample_valuesarrayNoExample values Meta uses during review
add_security_recommendationbooleanNoAUTHENTICATION templates — appends Meta's security line
code_expiration_minutesintegerNoAUTHENTICATION templates — 1–90; 0 omits the expiry footer

Update an existing template. Accepts the same body as Create.

Terminal window
PUT /api/templates/{id}

Delete a template.

Terminal window
DELETE /api/templates/{id}

Pull templates from Meta for one account and upsert them locally (matching on account + name + language, restoring soft-deleted rows). Requires templates:sync.

Terminal window
POST /api/templates/sync

The account name can be given either as an ?account= query parameter or in the body:

{
"whatsapp_account": "Main Business"
}

Omitting it returns 400 whatsapp_account is required; an unknown name returns 404. If Meta cannot be reached, the endpoint returns 502.

Submit a template for Meta approval.

Terminal window
POST /api/templates/{id}/publish
{
"status": "success",
"data": {
"id": "uuid",
"status": "PENDING"
}
}

Upload a media asset (image, video, or PDF) for a template's media header. This performs a resumable upload to Meta and returns a handle that you embed in the HEADER component's example when creating the template.

Terminal window
POST /api/templates/upload-media

Send the request as multipart/form-data:

FieldLocationRequiredDescription
accountform field or ?account= queryYesName of the WhatsApp account. The account must have an app_id configured.
fileform fileYesThe media file to upload (image/jpeg, image/png, video/mp4, or application/pdf).
Terminal window
curl -X POST "http://your-server:8080/api/templates/upload-media?account=main" \
-H "X-API-Key: whm_your_api_key" \
-F "file=@header.jpg"
{
"status": "success",
"data": {
"handle": "4::aW1hZ2UvanBlZw==:...",
"filename": "header.jpg",
"mime_type": "image/jpeg",
"size": 24576
}
}

Set the returned handle as header_content when creating a template whose header_type is IMAGE, VIDEO or DOCUMENT — Whatomate wraps it into Meta's example.header_handle on submission:

{
"header_type": "IMAGE",
"header_content": "4::aW1hZ2UvanBlZw==:..."
}

Meta's four components map onto flat fields in the Whatomate API:

Meta componentWhatomate fields
HEADERheader_type (TEXT | IMAGE | DOCUMENT | VIDEO | NONE) + header_content
BODYbody_content
FOOTERfooter_content
BUTTONSbuttons

Whatomate assembles the Meta components payload itself when you submit the template.

Use {{1}}, {{2}}, etc. for dynamic content:

Hello {{1}}, your order #{{2}} is ready for pickup at {{3}}.

Provide sample values as objects keyed by component + index:

{
"body_content": "Hello {{1}}, your order #{{2}} is ready!",
"sample_values": [
{ "component": "body", "index": 1, "value": "John" },
{ "component": "body", "index": 2, "value": "12345" }
]
}

Templates also support named parameters for better readability:

Hello {{customer_name}}, your order #{{order_id}} is ready for pickup at {{store_location}}.

When using named parameters, provide sample values with param_name:

{
"body_content": "Hello {{customer_name}}, your order #{{order_id}} is ready!",
"sample_values": [
{ "component": "body", "param_name": "customer_name", "value": "John" },
{ "component": "body", "param_name": "order_id", "value": "12345" }
]
}

You do not set parameter_format yourself — Whatomate detects non-numeric placeholders and sends parameter_format: "NAMED" to Meta on submission.

Templates can include URL buttons with dynamic variables. For example, a "Track Order" button with URL https://example.com/track/{{1}}:

{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Track Order",
"url": "https://example.com/track/{{1}}",
"example": "https://example.com/track/12345"
}
]
}

When sending a template with dynamic URL buttons, provide the dynamic values via the button_params field in the Send Template Message endpoint. The key is the zero-based button index:

{
"template_name": "order_shipped",
"template_params": { "1": "John", "2": "12345" },
"button_params": { "0": "12345" }
}