Skip to content

POST /v1/{waba_id}/templates

Creates a message template on a WhatsApp Business Account (WABA) and submits it to WhatsApp for review. New templates start as PENDING. WhatsApp usually reviews templates within 24 hours at most; review times are subject to WhatsApp’s policies. Poll GET /v1/templates/{template_id} to see the result.

The request body is Meta’s own Create Message Template body, forwarded to WhatsApp as sent — any component type or top-level field Meta accepts is accepted here, including kinds this page doesn’t show an example for (carousel, authentication, limited-time offer, catalog, flows).

POST /v1/{waba_id}/templates
Authorization: Bearer hello_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Terminal window
curl -X POST "$HELLO_API_URL/v1/<your-waba-id>/templates" \
-H "Authorization: Bearer $HELLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_shipped",
"language": "en",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, your order {{2}} has shipped and will arrive in 2-3 days.",
"example": { "body_text": [["Asha", "#10432"]] }
},
{ "type": "FOOTER", "text": "Reply STOP to opt out" },
{
"type": "BUTTONS",
"buttons": [
{ "type": "URL", "text": "Track order", "url": "https://example.com/track" }
]
}
]
}'

Use {{name}} placeholders and give examples by name:

{
"name": "login_otp_named",
"language": "en",
"category": "UTILITY",
"parameter_format": "NAMED",
"components": [
{
"type": "BODY",
"text": "Hi {{first_name}}, your appointment is on {{date}}.",
"example": {
"body_text_named_params": [
{ "param_name": "first_name", "example": "Asha" },
{ "param_name": "date", "example": "3 October" }
]
}
}
]
}

parameter_format is NAMED or POSITIONAL. Hello infers NAMED when you leave it out and use *_named_params; otherwise it’s worked out from the placeholders.

A media header needs a sample file. Put a public URL of the sample in example.header_handle, and Hello uploads it to WhatsApp for you:

{
"name": "diwali_sale",
"language": "en",
"category": "MARKETING",
"components": [
{
"type": "HEADER",
"format": "IMAGE",
"example": { "header_handle": ["https://example.com/samples/diwali-banner.jpg"] }
},
{ "type": "BODY", "text": "Our Diwali sale is live — 30% off everything until Sunday." }
]
}

A value in header_handle is treated as a URL to fetch (and swap for a Meta upload handle) when it looks like one — starts with a scheme, a /, or contains whitespace. An existing Meta media handle is left alone.

Field Type Required Description
name string Yes Lowercase letters, numbers and underscores only
language string Yes An official WhatsApp template language code, for example en, en_US, hi
category string Yes MARKETING, UTILITY or AUTHENTICATION
components array Yes Template components in WhatsApp’s format
parameter_format string No NAMED or POSITIONAL

Any other field Meta’s create body accepts (for example sub_category, message_send_ttl_seconds) is forwarded and stored unchanged.

Components:

type Fields
HEADER format: TEXT, IMAGE, VIDEO, DOCUMENT or LOCATION. text for text headers. example.header_handle (sample URL) for media headers.
BODY text (required), and example whenever the text has variables
FOOTER text
BUTTONS buttons[], each with type (QUICK_REPLY, URL, PHONE_NUMBER, COPY_CODE, and Meta’s other button kinds such as OTP and flow buttons), text, and url, phone_number or example as needed
CAROUSEL, and other component types Meta defines Forwarded and stored as sent

Meta’s own create response:

{ "id": "1234567890123456", "status": "PENDING", "category": "UTILITY" }
Status code Cause
400 VALIDATION_ERROR The body isn’t valid JSON, or fails Hello’s minimal checks
401 UNAUTHORIZED Missing, wrong or disabled API key
404 NOT_FOUND No such WABA in your organization
409 ACCOUNT_NOT_REGISTERED The WABA has no active, registered number
422 META_REJECTED WhatsApp rejected the template (duplicate name, invalid format, sample media it couldn’t fetch, and so on). meta_error carries Meta’s own error detail.
500 Temporary server error. Retry.
{
"message": "Meta rejected the template",
"code": "META_REJECTED",
"meta_error": {
"code": 100,
"error_user_title": "Invalid Template Format",
"error_user_msg": "Template content doesn't match the selected category."
}
}