Create a template

A template is a message Meta has reviewed and approved. It is the only thing you can send outside the 24-hour window, which makes it the backbone of any notification flow.

This page covers the templates you send yourself. The authentication template Verify uses is a different thing: it is created in Meta's WhatsApp Manager, not here — see Connect your WhatsApp account.

Listing templates

const page = await msg.templates.list({ limit: 20 });
// { data: [...], hasMore: true }

Or let the SDK page for you:

for await (const template of msg.templates.listAll({ status: "approved" })) {
  console.log(template.name);
}

Directly, listing is cursor-based: pass limit and starting_after, and stop when has_more is false.

Filters

limit1-100, default 20
statusOnly templates in that state
updatedAfterOnly templates changed since that instant
starting_afterThe previous page's last id

updatedAfter is what makes polling cheap. Meta can take days to decide on a template, and template.status_changed webhooks are the better answer — but if you poll, poll with this and you fetch only what actually moved.

for await (const template of msg.templates.listAll({ updatedAfter: lastCheckedAt })) {
  await recordStatus(template.id, template.status, template.rejectionReason);
}

The four statuses

StatusMeaning
pendingSubmitted, waiting on Meta.
processingMeta is reviewing it.
approvedUsable.
rejectedMeta refused it. rejectionReason says why.

Sending anything but approved fails with template_not_approved.

Creating a template

await msg.templates.create({
  name: "order_shipped",
  category: "UTILITY",
  language: "en_US",
  body: "Hi {{1}}, your order {{2}} has shipped.",
});

Check it first without spending a name:

const check = await msg.templates.validate({ name, category, language, body });
// { valid: false, issues: [...] }

Three things to know before you create one:

  • UTILITY only. create fixes the category, and marketing templates cannot be sent or listed through the API — a send returns template_category_not_allowed, and they never appear in templates.list. If Meta re-categorises an approved template as marketing it drops out of the list; fetching it by id still works, so you can see what happened.
  • Creating claims the name permanently in your WhatsApp account, so a test key cannot do it — see Test keys and the sandbox.
  • A deleted name stays reserved by Meta for about 30 days. Reusing it fails with template_name_taken.

Editing a template

await msg.templates.update("tpl_...", { body: "..." });

Only a template that was never submitted, or that Meta rejected, can be edited. Approved and in-review templates are frozen by Meta — editing one returns template_not_editable.

Create and edit need an Idempotency-Key

create and update are the only calls that require an Idempotency-Key header; everywhere else it is optional. A retry that accidentally created a second template would claim a name you cannot reuse for a month, so without the header both return 400 invalid_request.

The SDKs send one for you. Calling the API directly, put a UUID in the header and send the same one again if you retry.