Sending template messages
Templates are the only way to start a conversation or message a customer outside the 24-hour window — Meta requires a pre-approved template for any business-initiated message. You send one through POST /send-template, naming an approved template and filling its variables in order.
This page covers listing your approved templates and sending them. To author and submit templates for approval, see Creating templates; to send one template to many recipients, see Broadcasts.
With an app key, templates send on Meta Cloud numbers (native or your own Meta app). With your org API key they also send on partner numbers (GupShup, Plivo, AiSensy); WhatsApp Web numbers can't send templates.
List approved templates
Before sending, fetch the approved templates on a channel so you know each template's exact name, language, and how many variables it expects. GET /channels/{id}/templates returns approved templates only, each with a variables array whose entries carry the positional index of every {{n}} placeholder in the template's body (header and button values go in components).
curl https://api.telenow.ai/api/app-whatsapp/channels/b1e2c3d4-…/templates \
-H "Authorization: Bearer vai_app_…"
{
"success": true,
"data": {
"templates": [
{
"name": "order_update",
"language": "en_US",
"category": "UTILITY",
"variables": [ { "index": 1 }, { "index": 2 } ]
},
{
"name": "welcome",
"language": "en_US",
"category": "MARKETING",
"variables": []
}
]
}
}
The length of variables is how many strings you must pass — order_update above takes two, welcome takes none.
Send a template
POST /send-template takes { channelId, to, template, language?, variables }. language defaults to en_US; variables is a positional array of strings that fill {{1}}, {{2}}, … in order. A successful send returns the wamid, just like /send.
curl -X POST https://api.telenow.ai/api/app-whatsapp/send-template \
-H "Authorization: Bearer vai_app_…" \
-H "Content-Type: application/json" \
-d '{
"channelId": "b1e2c3d4-…",
"to": "+919876543210",
"template": "order_update",
"language": "en_US",
"variables": ["Asha", "#1234"]
}'
{ "success": true, "data": { "wamid": "wamid.HBgLMTQxNTU1MDEwMBUCABIYFj…" } }
| Field | Notes |
|---|---|
channelId | The channel id from GET /channels |
to | Recipient in E.164, e.g. +919876543210 |
template | Approved template name |
language | Optional; defaults to en_US. Must match the template's language |
variables | Positional strings that fill {{1}}, {{2}}, … in order — supply exactly as many as the template's variables array |
A template problem — the wrong number of values, a name or language WhatsApp doesn't have, a template Meta has paused — comes back with a plain-English explanation: as
400when you sentcomponents, or as502(whatsapp template send failed (400 Bad Request): …) when you sentvariables. Meta's code number isn't in the response. Values in the wrong order can't be detected — check them against the template.A number that replied STOP to you is blocked:
502… has opted out of messages from this business (they replied STOP). See opt-outs.
One-time-passcode (authentication) templates
An AUTHENTICATION template carries its code twice: in the message body ({{1}}) and on its Copy code button. On a Meta Cloud number, send the code once, as the only entry in variables. Telenow reads the template's definition and puts the same code on the button for you:
curl -X POST https://api.telenow.ai/api/app-whatsapp/send-template \
-H "Authorization: Bearer vai_app_…" \
-H "Content-Type: application/json" \
-d '{
"channelId": "b1e2c3d4-…",
"to": "+919876543210",
"template": "login_code",
"language": "en_US",
"variables": ["482913"]
}'
- Telenow reads the template from its synced copy of the number's templates. A template made outside Telenow is picked up within a few hours, or as soon as you open that number's Templates page. Until then, the send fails with WhatsApp's message that the button needs a parameter.
- If you send
componentsinstead, they go to WhatsApp exactly as written. Include both parts yourself: thebodyparameter, and{ "type": "button", "sub_type": "url", "index": "0", "parameters": [ { "type": "text", "text": "482913" } ] }with the button's position asindex. - On a partner number (Plivo, GupShup, AiSensy) nothing fills the button for you. Send it through a Meta Cloud number instead.
- The hub's template listing still shows these as
sendable: false, because the inbox and post-call senders have no code to send. Sending one through this API works as shown above.
Templates with a media header or dynamic buttons
A template whose header is an image/video/document, or that has a dynamic URL button, needs richer per-message data than a flat variables array can carry. For these, pass a components array instead — this is Meta's runtime component format, where each object targets a template part (header, body, button) and lists its parameters.
For a media header, first upload the asset with POST /channels/{id}/media to get a mediaId (a UUID), then put that mediaId in the header parameter. Telenow translates it into WhatsApp's own media id at send time — you never handle a raw Meta media handle here. The upload must be on the same channel; if it's gone, the send fails with 502 (the image attached to this template is no longer available on this channel…). A value that isn't a UUID is passed to WhatsApp unchanged as a Meta media id, and a public link works too.
# 1) Upload the header image → mediaId
curl -X POST https://api.telenow.ai/api/app-whatsapp/channels/b1e2c3d4-…/media \
-H "Authorization: Bearer vai_app_…" \
-F "file=@/path/to/receipt.png"
{ "success": true, "data": { "mediaId": "5f0c2b1e-8d4a-4b6e-9c3f-2a7d1e0b9c84", "mime": "image/png", "filename": "receipt.png", "bytes": 84213 } }
# 2) Send the template, referencing that mediaId in the header component
curl -X POST https://api.telenow.ai/api/app-whatsapp/send-template \
-H "Authorization: Bearer vai_app_…" \
-H "Content-Type: application/json" \
-d '{
"channelId": "b1e2c3d4-…",
"to": "+919876543210",
"template": "order_receipt",
"language": "en_US",
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "id": "5f0c2b1e-8d4a-4b6e-9c3f-2a7d1e0b9c84" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Asha" }
]
}
]
}'
{ "success": true, "data": { "wamid": "wamid.HBgLMTQxNTU1MDEwMBUCABIYFj…" } }
- Use
componentsinstead ofvariables— put your body substitutions in thebodycomponent'sparameters, not in a separatevariablesarray. - For a video or document header, swap the parameter to
{ "type": "video", "video": { "id": "<mediaId>" } }or{ "type": "document", "document": { "id": "<mediaId>" } }. - For a dynamic URL button, add a
buttoncomponent, e.g.{ "type": "button", "sub_type": "url", "index": 0, "parameters": [ { "type": "text", "text": "1234" } ] }— the text fills the{{1}}in the button's URL. componentsneeds a Meta Cloud number; on any other number it returns502.
With your org API key
With your org API key (X-API-Key: vai_live_…) the channel goes in the path, and the body is the same minus channelId:
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/whatsapp-hub/channels/{channelId}/send-template \
-H "X-API-Key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{
"to": "+919876543210",
"template": "order_update",
"language": "en_US",
"variables": ["Asha", "#1234"]
}'
{ "success": true, "data": { "wamid": "wamid.HBgLMTQxNTU1MDEwMBUCABIYFj…" } }
- Inside a conversation:
POST /api/orgs/{orgId}/whatsapp-hub/threads/{threadId}/send-template— same body withoutto. - Which templates a number can send:
GET /api/orgs/{orgId}/whatsapp-hub/channels/{channelId}/templatesworks on every provider number. Each entry hasref(the value to send astemplate),label,language,category,body,variableCountandsendable.sendableisfalse, with anunsupportedReason, when a plainvariablessend can't fill the template (a media header or a dynamic button) — on a Meta Cloud number, send those withcomponents. Meta Cloud numbers list approved templates; partner numbers list what the provider returns. To see every status, including pending and rejected, use the template management routes. - Partner numbers take
variablesonly (componentsis Meta Cloud only). On AiSensy,templateis the API campaign name. - Sends need an
owner,adminordeveloperkey.
Related
- Creating templates — author, submit for approval, and add media headers or dynamic buttons.
- Broadcasts — send a template to many recipients at once.
- Media messages — upload assets and get a
mediaId. - Webhooks & events — track
sent/delivered/read/failedstatus perwamid. - WhatsApp overview — connect a number and find your
channelId.