Templates
Create, list, and import Meta-approved WhatsApp message templates
Templates
WhatsApp templates are required for initiating conversations outside the 24-hour messaging window. They must be submitted to Meta for approval before use.
List Templates
const { data } = await zernio.whatsapp.getWhatsAppTemplates({
query: { accountId: 'YOUR_ACCOUNT_ID' }
});
data.templates.forEach(t => console.log(`${t.name} (${t.status}) - ${t.language}`));Create a Template
const { data } = await zernio.whatsapp.createWhatsAppTemplate({
body: {
accountId: 'YOUR_ACCOUNT_ID',
name: 'order_confirmation',
category: 'UTILITY',
language: 'en',
components: [{ type: 'body', text: 'Hi {{1}}, your order {{2}} has been confirmed!' }]
}
});
console.log(`Template created: ${data.template.name} (${data.template.status})`);Templates are reviewed by Meta and can take up to 24 hours to be approved. Only approved templates can be used for sending messages.
Delivery TTL
If a message cannot be delivered (phone off, no signal), WhatsApp keeps retrying for the template's time-to-live and drops it afterwards. Meta defaults to 10 minutes for AUTHENTICATION templates and 30 days for everything else, which is far too long for a delivery code or an order-ready notice. Set message_send_ttl_seconds on Create template to shorten it, or on either update endpoint to change it later (components is optional there when only the TTL changes).
| Category | Allowed range | Default |
|---|---|---|
AUTHENTICATION | 30 to 900 seconds | 600 |
UTILITY | 30 to 43200 seconds (12 hours) | 30 days |
MARKETING | 43200 to 2592000 seconds (12 hours to 30 days) | 30 days |
-1 keeps the 30-day default on AUTHENTICATION and UTILITY, on create only (Meta rejects it on an edit). A value outside the category's range is a 400 with param: message_send_ttl_seconds. A TTL-only update does not send an approved template back to review; a component update does. Template reads return the field only while a custom TTL is set; if Meta recategorises a template, it clears the TTL, so read it back after a category change.
curl -X POST https://zernio.com/api/v1/whatsapp/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_ACCOUNT_ID",
"name": "order_ready",
"category": "UTILITY",
"language": "en",
"message_send_ttl_seconds": 600,
"components": [{"type": "body", "text": "Hi {{1}}, your order {{2}} is ready for pickup."}]
}'Template statuses
A template's status is Meta's review verdict, forwarded verbatim. Only an APPROVED template can be sent.
status | Meaning |
|---|---|
PENDING | Under Meta review. |
APPROVED | Live — usable for sending. |
REJECTED | Meta declined it. Fix the content and resubmit, or appeal the decision. |
IN_APPEAL | A rejection appeal is being re-reviewed. |
PAUSED | Meta paused delivery (typically negative recipient feedback); the template can't be sent while paused. |
DISABLED | Disabled by Meta; the template can no longer be sent. |
PENDING_DELETION | Delete requested; the template is removed after a 24-hour grace period. |
Don't poll the template list to catch verdicts: the whatsapp.template.status_updated webhook fires whenever Meta finishes a (re)review of any template on the connected WABA, carrying the new status and Meta's reason ("NONE" on approval, an explanation string on rejection).
Import from the Template Library
Instead of writing a template, you can create one from Meta's pre-approved Template Library — these skip the review wait. Pass library_template_name instead of components.
Look the template up first to see its structure: a library template with URL or PHONE_NUMBER buttons requires a matching library_template_button_inputs array at create time (one entry per button, in order), or Meta rejects it with "give the same number of button inputs to match the library buttons." A library template usually exists in many languages — pass language to pick the right variant.
// 1. Look up the library template (to see its buttons + the exact language)
const { data: lib } = await zernio.whatsapp.getWhatsAppLibraryTemplate({
query: { accountId: 'YOUR_ACCOUNT_ID', name: 'account_creation_confirmation_3', language: 'en_US' }
});
// 2. Create it, supplying inputs for its URL / PHONE_NUMBER buttons
const { data } = await zernio.whatsapp.createWhatsAppTemplate({
body: {
accountId: 'YOUR_ACCOUNT_ID',
name: 'account_creation_confirmation_3',
category: 'UTILITY',
language: lib.template.language, // use the library template's actual code
library_template_name: 'account_creation_confirmation_3',
library_template_button_inputs: [
{ type: 'URL', url: { base_url: 'https://your-site.com/account', url_suffix_example: 'https://your-site.com/account' } }
]
}
});