Send template
Send approved template notifications (order updates, reminders).
Use templates when you need to reach out first — order updates, OTP codes, appointment reminders — especially outside the 24-hour reply window. Each template is pre-approved by Meta.
A template send needs more than name and language. Pass components that match how the template was built: body variables, optional header image or text, and button URL parameters when the template defines them.
Before you send
Create and approve the template
Create the template in the Wazapin app or sync from Meta on official channels. Status must be approved.
GET /v1/templates lists templates for your workspace. See Channel support for official vs unofficial behavior.
Read the template structure
Open the template in the app and note:
- Header — none, text (
{{1}}), image, video, or document - Body — static text with
{{1}},{{2}}, … placeholders - Buttons — quick reply, URL (may include
{{1}}in the link), copy code, etc.
Your components array must mirror these sections in order.
Build the components array
Each section becomes one object in content.template.components:
Component type |
When required |
|---|---|
header |
Template has a dynamic header (text, image, video, document) |
body |
Template body has {{n}} variables |
button |
URL or other dynamic button parameters (sub_type, index) |
Omit a section if the template has no variables in that part.
Request shape
All template sends use POST /v1/messages with type: "template" and a nested content.template object (Meta-compatible shape).
| Field | Required | Description |
|---|---|---|
channel_id |
Yes | Connected WhatsApp channel |
to |
Yes | Recipient phone (international format) |
type |
Yes | template |
content.template.name |
Yes | Approved template name |
content.template.language.code |
Yes | Locale, e.g. en_US, id |
content.template.components |
Often | Parameters per header/body/button |
Example: body variables only
Template body: Hi {{1}}, your order {{2}} is on the way.
curl -X POST "https://api.wazapin.com/v1/messages" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel_id": "wzp_abc123",
"to": "6281234567890",
"type": "template",
"content": {
"template": {
"name": "order_shipped",
"language": { "code": "en_US" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "John" },
{ "type": "text", "text": "ORD-42" }
]
}
]
}
}
}'import { WazapinClient, templateMessage } from "@wazapin/sdk";
const wazapin = new WazapinClient({ apiKey: process.env.WAZAPIN_API_KEY! });
const { data: message } = await wazapin.messages.send(
templateMessage({
channel_id: "wzp_abc123",
to: "6281234567890",
name: "order_shipped",
language: "en_US",
components: [
{
type: "body",
parameters: [
{ type: "text", text: "John" },
{ type: "text", text: "ORD-42" },
],
},
],
}),
);
console.log(message.id);import requests
response = requests.post(
"https://api.wazapin.com/v1/messages",
headers={"X-Api-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
json={
"channel_id": "wzp_abc123",
"to": "6281234567890",
"type": "template",
"content": {
"template": {
"name": "order_shipped",
"language": {"code": "en_US"},
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "John"},
{"type": "text", "text": "ORD-42"},
],
}
],
}
},
},
timeout=30,
)
response.raise_for_status()
print(response.json())Example: header image + body
Template with an image header and one body variable.
{
"channel_id": "wzp_abc123",
"to": "6281234567890",
"type": "template",
"content": {
"template": {
"name": "promo_with_image",
"language": { "code": "id" },
"components": [
{
"type": "header",
"parameters": [
{
"type": "image",
"image": { "link": "https://cdn.example.com/promo.jpg" }
}
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Ramadan Sale" }
]
}
]
}
}
}
Example: header text variable
Template header text: Hello {{1}} — the header component lives inside content.template.components.
{
"channel_id": "wzp_abc123",
"to": "6281234567890",
"type": "template",
"content": {
"template": {
"name": "greeting_header",
"language": { "code": "en_US" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "text", "text": "John" }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Your appointment is confirmed." }
]
}
]
}
}
}
Example: dynamic URL button
Template with a URL button containing {{1}} in the path — button component nested in the same components array.
{
"channel_id": "wzp_abc123",
"to": "6281234567890",
"type": "template",
"content": {
"template": {
"name": "order_tracking",
"language": { "code": "en_US" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "John" },
{ "type": "text", "text": "ORD-42" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "ORD-42" }
]
}
]
}
}
}
index is the zero-based button index in the template definition. sub_type matches the button type Meta approved (url, quick_reply, copy_code, etc.).
List and sync templates with wazapin.templates.list() and wazapin.templates.sync(). See SDK templates (advanced).
Common mistakes
| Mistake | Fix |
|---|---|
| Template not approved | Wait for Meta approval or pick another name |
Wrong language.code |
Must match the template locale exactly (en_US vs en) |
Missing components |
Add parameters for every {{n}} in header/body/buttons |
Flat template_name in content |
Use nested content.template.name |
| Header image URL not HTTPS | Use a public HTTPS URL Meta can fetch |
Related
- Sending overview — session vs template
- Example: send template
- Send a message — API reference and playground
- Channel support — unofficial channel fallback behavior