Skip to content

Send a template with a header and footer

A WhatsApp template message can have up to four parts:

Part Required What it holds
Header No A short title, or an image, video, document or location shown above the text
Body Yes The main text, with optional {{variables}}
Footer No A short line of small grey text, for example an opt-out hint
Buttons No See Buttons

The header’s type and the footer’s text are fixed when the template is created and approved. When you send, you only supply values for the variable parts: header media, header text variables and body variables.

{
"name": "appointment_reminder",
"language": "en",
"category": "UTILITY",
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Reminder for {{1}}",
"example": { "header_text": ["Asha"] }
},
{
"type": "BODY",
"text": "Your appointment with {{1}} is on {{2}} at {{3}}.",
"example": { "body_text": [["Dr. Rao", "3 October", "10:30 am"]] }
},
{ "type": "FOOTER", "text": "Reply STOP to unsubscribe" }
]
}

The footer is static, so it needs nothing at send time. Supply the header variable in a header component, and the body variables in a body component:

Terminal window
curl -X POST "$HELLO_API_URL/v1/<your-phone-number-id>/messages" \
-H "Authorization: Bearer $HELLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919999999999",
"type": "template",
"template": {
"name": "appointment_reminder",
"language": { "code": "en" },
"components": [
{
"type": "header",
"parameters": [{ "type": "text", "text": "Asha" }]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Dr. Rao" },
{ "type": "text", "text": "3 October" },
{ "type": "text", "text": "10:30 am" }
]
}
]
}
}'

The customer sees:

Reminder for Asha Your appointment with Dr. Rao is on 3 October at 10:30 am. Reply STOP to unsubscribe

A text header without a variable (for example "text": "Appointment reminder") needs no header component when sending.

A media header needs a sample file for WhatsApp’s review. Pass a public URL of the sample in example.header_handle; Hello fetches it and uploads it to WhatsApp for you.

{
"name": "order_dispatched",
"language": "en",
"category": "UTILITY",
"components": [
{
"type": "HEADER",
"format": "IMAGE",
"example": { "header_handle": ["https://example.com/samples/parcel.jpg"] }
},
{
"type": "BODY",
"text": "Hi {{1}}, order {{2}} is on its way.",
"example": { "body_text": [["Asha", "#10432"]] }
},
{ "type": "FOOTER", "text": "Thanks for shopping with us" }
]
}

Every send must supply the actual media for a media header, as link or as an uploaded id:

{
"to": "+919999999999",
"type": "template",
"template": {
"name": "order_dispatched",
"language": { "code": "en" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://example.com/orders/10432/parcel.jpg" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Asha" },
{ "type": "text", "text": "#10432" }
]
}
]
}
}

For other header formats, swap the parameter:

Header format Send parameter
IMAGE { "type": "image", "image": { "link": "…" } } or { "image": { "id": "<media_id>" } }
VIDEO { "type": "video", "video": { "link": "…" } }
DOCUMENT { "type": "document", "document": { "link": "…", "filename": "Invoice.pdf" } }
LOCATION { "type": "location", "location": { "latitude": "12.9716", "longitude": "77.5946", "name": "…", "address": "…" } }

Media must meet the supported media limits. In a location header, latitude and longitude are strings.

These are WhatsApp’s rules, checked when the template is reviewed:

  • Text header: up to 60 characters, at most one variable.
  • Footer: up to 60 characters, no variables.
  • Body: up to 1,024 characters.

If the header or body parameters don’t match the approved template (wrong count, missing media for a media header), WhatsApp rejects the message. The send is queued first, so the rejection shows up as status: "failed" with an error_message on GET /v1/messages/{id}, or straight away as SEND_FAILED with ?sync=true.