Sending messages
Send an approved template by id, or by name plus language.
Outside the 24-hour window, WhatsApp only
allows approved templates. A template is submitted to Meta for review, moves
through an approval lifecycle, and — once APPROVED — can be sent to opted-in
contacts. This page covers creating and tracking templates; see
Sending messages for sending an
approved one. Read paths need templates:read; create needs templates:write.
Templates can only be submitted on a dedicated WhatsApp channel whose Meta
business account is verified and has a Meta payment method. The shared
Wabery sandbox cannot submit templates — they belong to Wabery’s own account.
Read why WhatsApp templates need a payment method
if Wabery reports NO_PAYMENT_METHOD. Check readiness first:
const channel = await wabery.channels.get("channel_...");if (!channel.publish_readiness?.can_submit_templates) { console.log("Blocked by:", channel.publish_readiness?.blockers); // e.g. ["BUSINESS_NOT_VERIFIED", "NO_PAYMENT_METHOD"]}Submitting when not ready returns 412 with the same publish_readiness
blockers — Wabery rejects it rather than queueing work that Meta will refuse.
One call creates the template and submits it to Meta. category is one of
UTILITY, MARKETING, or AUTHENTICATION; components follow Meta’s template
component schema (header, body, footer, buttons).
const template = await wabery.templates.create({ channelId: "channel_...", name: "order_shipped", // a-z, 0-9, underscores language: "en", category: "UTILITY", components: [ { type: "BODY", text: "Your order {{1}} has shipped." }, ],});
console.log(template.status); // "PENDING" — review has startedcurl https://api.wabery.com/v1/templates \ -H "Authorization: Bearer $WABERY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "channel_...", "name": "order_shipped", "language": "en", "category": "UTILITY", "components": [ { "type": "BODY", "text": "Your order {{1}} has shipped." } ] }'A template response:
{ "object": "whatsapp_template", "id": "tmpl_...", "channel_id": "channel_...", "meta_template_id": "1234567890", "name": "order_shipped", "language": "en", "category": "UTILITY", "status": "PENDING", "submitted_at": "2026-06-20T14:00:00Z"}Review is asynchronous and can take up to 24 hours. status moves through:
| Status | Meaning |
|---|---|
PENDING |
Submitted to Meta; awaiting review. |
APPROVED |
Ready to send. |
REJECTED |
Meta declined it; see rejection_reason. |
DISABLED |
Previously approved, later disabled (e.g. quality drop). |
Track it in one of three ways — prefer the first two in production over blocking a request:
1. Webhook (recommended). Meta status changes arrive as a signed
template.status webhook — no polling.
2. On-demand refresh. status(id) forces a live Meta status check:
const latest = await wabery.templates.status("tmpl_..."); // GET with refreshwabery templates status "tmpl_..."3. Block until resolved. For scripts and CI, waitUntilApproved polls until
APPROVED, throwing if the template is REJECTED/DISABLED or the timeout
elapses:
const approved = await wabery.templates.waitUntilApproved("tmpl_...", { timeoutMs: 60 * 60 * 1000, // default 24h intervalMs: 60_000,});wabery templates wait "tmpl_..." --wait=86400Filter by channelId and/or status. Requires templates:read.
const { data } = await wabery.templates.list({ channelId: "channel_...", status: "APPROVED",});Once a template is APPROVED, send it — including flow-type templates for
proactive WhatsApp Flows — from
Sending messages.
Sending messages
Send an approved template by id, or by name plus language.
WhatsApp Flows
Flow-type templates open a Flow proactively, outside the 24-hour window.
Webhooks & events
Track approval with signed template.status events.