Templates
Understanding message templates for multi-channel messaging. Reusable structures for WhatsApp, SMS, Telegram and Email with variables, categories and
Templates
Templates are reusable message formats for sending structured messages across WhatsApp, SMS, Telegram, Instagram, and Email. They are especially important for WhatsApp, which requires template approval for business-initiated conversations.
Why Templates?
Templates solve three key problems:
- WhatsApp Compliance: Meta requires businesses to use approved templates when initiating conversations outside the 24-hour window
- Consistency: Ensure your transactional messages follow a consistent format
- Multi-Channel Delivery: Define one template with channel-specific bodies, then send it on any channel
Template Structure
A template consists of:
JSON{ "id": "tmpl_abc123", "name": "order_confirmation", "language": "en", "body": "Hi {{1}}, your order {{2}} has been confirmed!", "category": "UTILITY", "variables": ["customer_name", "order_id"], "status": "approved" }
Fields
| Field | Description |
|---|---|
name | Unique identifier (lowercase, underscores) |
language | Language code (e.g., "en", "es", "pt") |
body | Message text with variable placeholders |
category | WhatsApp category (required for WhatsApp submission, optional for other channels) |
variables | List of variable names for documentation |
status | Approval status |
Template Variables
Templates support dynamic content through variables. Two formats are supported:
Numbered Variables (WhatsApp Native)
Use {{1}}, {{2}}, {{3}} for WhatsApp compatibility:
Code"Hi {{1}}, your order {{2}} ships on {{3}}"
Named Variables
Use {{name}} for better readability:
Code"Hi {{customer_name}}, your order {{order_id}} ships on {{date}}"
When you create a template in Zavu, named variables are automatically
converted to numbered format ({{1}}, {{2}}) at the moment it is submitted to
Meta. The template is therefore approved as positional at Meta, even though
Zavu keeps the readable named body for display.
Templates imported from an existing WhatsApp Business Account keep whatever
format Meta has on file — these can be named ({{customer_name}}) or
positional ({{1}}). Zavu detects the format per template and sends the
correct payload either way.
Contact Variables
Use {{contact.*}} variables to auto-resolve values from the recipient's contact metadata:
| Variable | Resolved From |
|---|---|
{{contact.first_name}} | Contact metadata first_name |
{{contact.last_name}} | Contact metadata last_name |
{{contact.phone}} | Contact's phone number |
{{contact.email}} | Contact's primary email |
{{contact.country}} | Contact's country code |
{{contact.custom_field}} | Any custom metadata field |
Code"Hola {{contact.first_name}}, tu pedido esta listo."
When the message is sent, Zavu looks up the recipient's contact record and replaces these variables automatically. If the contact doesn't exist or the field is empty, the variable is replaced with an empty string.
WhatsApp Categories
WhatsApp requires every template to be categorized:
| Category | Use Case | Examples |
|---|---|---|
| UTILITY | Transactional messages | Order confirmations, shipping updates, appointment reminders |
| MARKETING | Promotional content | Sales, offers, newsletters |
| AUTHENTICATION | Verification codes | OTPs, login codes, 2FA |
Authentication Templates
Authentication templates are used for sending verification codes (OTPs) to users. These templates have special requirements and behavior.
Requirements
How Authentication Templates Work
Unlike regular templates, authentication templates have a pre-defined message format controlled by Meta. When you create an authentication template:
- The message body is automatically generated by Meta
- The format is:
{{1}} is your verification code. - You cannot customize the body text
OTP Button Types
Authentication templates support two types of OTP buttons:
| Button Type | Description | Use Case |
|---|---|---|
| COPY_CODE | Shows a "Copy Code" button | User manually copies and pastes the code |
| ONE_TAP | Enables Android autofill | Automatic code entry on Android devices |
ONE_TAP Button Requirements
For ONE_TAP buttons (Android autofill), you must provide:
JSON{ "buttons": [{ "type": "otp", "text": "Autofill", "otpType": "ONE_TAP", "packageName": "com.yourapp.package", "signatureHash": "your_app_signature_hash" }] }
| Field | Description |
|---|---|
packageName | Your Android app's package name |
signatureHash | Your Android app's signature hash for SMS verification |
Optional Security Features
Authentication templates can include:
| Feature | Description |
|---|---|
addSecurityRecommendation | Adds "Do not share this code with anyone" disclaimer |
codeExpirationMinutes | Shows expiration time (1-90 minutes) in footer |
Creating an Authentication Template
terminalcurl -X POST https://api.zavu.dev/v1/templates \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "verification_code", "language": "en", "body": "", "whatsappCategory": "AUTHENTICATION", "buttons": [{ "type": "otp", "text": "Copy Code", "otpType": "COPY_CODE" }], "addSecurityRecommendation": true, "codeExpirationMinutes": 10 }'
WhatsApp Approval Workflow
WhatsApp templates must be approved by Meta before use:
CodeCreate Template (draft) | v Submit to WhatsApp (pending) | v Meta Reviews (24-48 hours) | +---+---+ | | Approved Rejected | | v v Ready Fix & Resubmit
Template Statuses
| Status | Description |
|---|---|
draft | Created but not submitted |
pending | Submitted, awaiting Meta review |
approved | Ready to use |
rejected | Rejected by Meta (see reason) |
Creating Templates
Via API
terminalcurl -X POST https://api.zavu.dev/v1/templates \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "order_shipped", "language": "en", "body": "Hi {{1}}, your order {{2}} has shipped and will arrive by {{3}}.", "whatsappCategory": "UTILITY", "variables": ["customer_name", "order_id", "delivery_date"] }'
Response
JSON{ "id": "tmpl_xyz789", "name": "order_shipped", "language": "en", "body": "Hi {{1}}, your order {{2}} has shipped...", "category": "UTILITY", "status": "draft", "variables": ["customer_name", "order_id", "delivery_date"], "createdAt": "2025-01-15T10:30:00Z" }
Rich Template Components
WhatsApp templates can include rich components:
Header
Add a header with text, image, video, or document:
JSON{ "headerType": "image", "headerContent": "https://example.com/logo.png" }
Footer
Add a footer text:
JSON{ "footer": "Reply STOP to unsubscribe" }
Buttons
Add interactive buttons:
JSON{ "buttons": [ { "type": "quick_reply", "text": "Track Order" }, { "type": "url", "text": "View Details", "url": "https://example.com/orders/{{1}}" } ] }
Sending Template Messages
To send a template message, specify the template ID and variables:
await client.messages.send({
to: "+14155551234",
messageType: "template",
content: {
templateId: "tmpl_abc123",
templateVariables: {
"1": "John",
"2": "ORD-12345",
"3": "January 20th"
}
}
});
Templates with Dynamic URL Buttons
If your template has a URL button with a {{1}} placeholder (e.g., https://example.com/orders/{{1}}), pass the value through templateButtonVariables. Keys are the button index (0-based) in the template's buttons array.
terminalcurl -X POST https://api.zavu.dev/v1/messages \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+14155551234", "messageType": "template", "content": { "templateId": "tmpl_abc123", "templateVariables": { "1": "John" }, "templateButtonVariables": { "0": "ORD-12345" } } }'
| Field | Keys | What it substitutes |
|---|---|---|
templateVariables | Position in body ("1", "2", ...) | Body placeholders |
templateButtonVariables | Button index ("0", "1", "2") | The {{1}} placeholder inside that button's URL |
Multi-Channel Templates
Templates support channel-specific bodies. When a message is sent, Zavu uses the channel-specific body if available, falling back to the default body.
JSON{ "name": "order_confirmation", "body": "Hi {{1}}, your order {{2}} is confirmed!", "smsBody": "Order {{2}} confirmed for {{1}}. Track at example.com", "telegramBody": "Hi {{1}}, your order {{2}} is confirmed! Check your Telegram for updates.", "instagramBody": "Hi {{1}}! Order {{2}} confirmed.", "emailSubject": "Order {{2}} Confirmed", "emailHtmlBody": "<h2>Order Confirmed</h2><p>Hi {{1}}...</p>" }
| Field | Channel | Fallback |
|---|---|---|
body | Default for all channels | - |
smsBody | SMS, SMS One-Way | body |
telegramBody | Telegram | body |
instagramBody | body | |
emailSubject | Email subject | - |
emailHtmlBody | Email HTML body | body (plain text) |
Sending Templates on Different Channels
curl -X POST https://api.zavu.dev/v1/messages \
-H "Authorization: Bearer $ZAVUDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "123456789",
"channel": "telegram",
"messageType": "template",
"content": {
"templateId": "tmpl_abc123",
"templateVariables": { "1": "John", "2": "ORD-12345" }
}
}'
Managing Templates
List Templates
terminalcurl https://api.zavu.dev/v1/templates \ -H "Authorization: Bearer zv_live_xxx"
Get Template
terminalcurl https://api.zavu.dev/v1/templates/tmpl_abc123 \ -H "Authorization: Bearer zv_live_xxx"
Delete Template
terminalcurl -X DELETE https://api.zavu.dev/v1/templates/tmpl_abc123 \ -H "Authorization: Bearer zv_live_xxx"
Best Practices
WhatsApp has character limits. Keep your templates concise and actionable.
For transactional messages, always use UTILITY to improve approval chances.
Create templates early in development. Approval can take 24-48 hours.
If rejected, review Meta's guidelines, fix the issue, and resubmit.
Common Rejection Reasons
| Reason | Solution |
|---|---|
| Promotional content in UTILITY | Change category to MARKETING or remove promotional language |
| Missing variable examples | Provide clear example values for each variable |
| Inappropriate content | Review WhatsApp commerce policy |
| Poor grammar/spelling | Proofread and fix language errors |
Next Steps
Deep dive into WhatsApp template features
Learn how templates work with senders
API Reference
Manage templates programmatically:
- List templates — fetch all templates for your project
- Get a template — retrieve template details by ID
- Delete a template — remove an unapproved template