Skip to content

Template Status Webhook

Also called: message_template_status_update · Template status update · Template approval webhook

The message_template_status_update webhook reports each change in a template's state — approved, rejected, paused, disabled, archived — with the reason.

Reviewed

What is the template status webhook?

The template status webhook is the message_template_status_update field. Meta sends it whenever a message template changes state: approved or rejected after review, paused or disabled because of quality, flagged, appealed, archived after long inactivity, or deleted. Each notification carries event (the new state), message_template_id, message_template_name, message_template_language, reason and message_template_category, inside an entry whose id is the WABA.

Meta's reference lists more than a dozen event values — APPROVED, REJECTED, PENDING, PAUSED, DISABLED, FLAGGED, IN_APPEAL, REINSTATED, LOCKED, LIMIT_EXCEEDED, ARCHIVED, UNARCHIVED, DELETED and PENDING_DELETION — and adds new ones over time. reason is NONE (used on pauses), INVALID_FORMAT, ABUSIVE_CONTENT, PROMOTIONAL, SCAM, INCORRECT_CATEGORY or TAG_CONTENT_MISMATCH (the older CATEGORY_NOT_AVAILABLE is deprecated), and null for a scheduled deletion. Three optional objects add detail: rejection_info with a reason and a recommendation, only for INVALID_FORMAT; disable_info.disable_date on a disable; and other_info with a title (FIRST_PAUSE, SECOND_PAUSE, RATE_LIMITING_PAUSE, UNPAUSE or DISABLED) and a human-readable description on pause-related events.

Template approval describes the review process; this webhook is the event that reports its outcome — an approval, a template rejection or a template appeal under way — and everything that happens to the message template afterwards, such as template pausing. It is also distinct from its two siblings: the template quality webhook reports score moves between GREEN, YELLOW and RED, and the template category update webhook reports a move between marketing, utility and authentication. All three go to the app's default callback URL, whatever overrides are set.

Key takeaways

  • The template status webhook (message_template_status_update) reports every change in a template's state, from approval to deletion.
  • Route on event: APPROVED releases sends, PAUSED is temporary, DISABLED is final and REJECTED carries a reason.
  • A rejection_info object with a fix recommendation appears only on INVALID_FORMAT rejections.
  • Review is automatic and can take up to 24 hours, so schedule campaigns on the APPROVED webhook, not the create call.
  • Templates unused for 12 months are archived, then deleted 28 days later unless unarchived.

See InfiQ pricing

Interactive stop

Walk through a template status handler

6 / 6
export async function onTemplateStatus(entry, change) {
const v = change.value;
const tpl = await templates.byId(v.message_template_id);
await tpl.recordStatus(v.event, v.reason, v.message_template_category, entry.time);
switch (v.event) {
case "APPROVED":
case "REINSTATED":
case "UNARCHIVED":
await campaigns.release(tpl.id);
break;
case "PAUSED":
// other_info.title: FIRST_PAUSE, SECOND_PAUSE or RATE_LIMITING_PAUSE
await campaigns.hold(tpl.id, v.other_info?.title);
break;
case "DISABLED":
await campaigns.cancel(tpl.id, v.disable_info?.disable_date);
break;
case "REJECTED":
await alerts.notify(tpl.owner, {
reason: v.reason, // e.g. INVALID_FORMAT, PROMOTIONAL
detail: v.rejection_info?.reason, // only for INVALID_FORMAT
fix: v.rejection_info?.recommendation,
});
break;
case "FLAGGED":
await alerts.warn(tpl.owner, "Negative feedback: may be disabled soon");
break;
default: // PENDING, IN_APPEAL, ARCHIVED, DELETED...
await audit.log("message_template_status_update", v);
}
}

Step 6 of 6 · lines 29–30

PENDING, IN_APPEAL, LOCKED, LIMIT_EXCEEDED, ARCHIVED, DELETED and new values are logged, never dropped.

Jump to
A handler that turns every message_template_status_update into an action, and the paused payload it reads. Step through the highlighted lines to see what each branch is for.

Handler (Node.js), step 6 of 6, lines 29–30: PENDING, IN_APPEAL, LOCKED, LIMIT_EXCEEDED, ARCHIVED, DELETED and new values are logged, never dropped.

How does the template status webhook work?

  1. Subscribe on the default callback

    Tick message_template_status_update in the app's webhook fields. It is not affected by WABA or phone-number overrides, so the default endpoint must stay live.

  2. Store IDs at creation

    Creating a template returns its id, status and category. Store the ID per language; status webhooks reference it, and one name can exist in many languages.

  3. Gate sends on state

    Let campaigns use a template only after APPROVED (or REINSTATED/UNARCHIVED). Review is automatic and can take up to 24 hours, so schedule launches with that margin.

  4. Hold on pauses, replace on disables

    PAUSED is temporary — 3 hours the first time, 6 the second — so hold, and resume only when a later status event lifts the pause (Meta marks it with other_info.title: UNPAUSE), not on a timer. DISABLED is final: switch automations to an approved replacement.

  5. Show rejections to the author

    Surface reason and, for INVALID_FORMAT, rejection_info.recommendation in your template editor, then let the author fix and resubmit or appeal (appeals are decided within 24 hours).

  6. Decide on archived templates

    ARCHIVED means 12 months without use; the template is deleted 28 days later unless unarchived. Unarchive the ones automations still need.

The template status webhook: key facts and limits

Webhook field
message_template_status_update
Event values
More than a dozen (APPROVED, REJECTED, PAUSED, DISABLED…)
Rejection detail
rejection_info only for INVALID_FORMAT
Pause titles
FIRST_PAUSE, SECOND_PAUSE, RATE_LIMITING_PAUSE, UNPAUSE, DISABLED
Review time
Automatic, up to 24 hours
Archiving
After 12 months unused; deleted 28 days later unless unarchived
Callback override
Not supported: always the app's default URL

The template status webhook in code

export async function onTemplateStatus(entry, change) {
const v = change.value;
const tpl = await templates.byId(v.message_template_id);
await tpl.recordStatus(v.event, v.reason, v.message_template_category, entry.time);
switch (v.event) {
case "APPROVED":
case "REINSTATED":
case "UNARCHIVED":
await campaigns.release(tpl.id);
break;
case "PAUSED":
// other_info.title: FIRST_PAUSE, SECOND_PAUSE or RATE_LIMITING_PAUSE
await campaigns.hold(tpl.id, v.other_info?.title);
break;
case "DISABLED":
await campaigns.cancel(tpl.id, v.disable_info?.disable_date);
break;
case "REJECTED":
await alerts.notify(tpl.owner, {
reason: v.reason, // e.g. INVALID_FORMAT, PROMOTIONAL
detail: v.rejection_info?.reason, // only for INVALID_FORMAT
fix: v.rejection_info?.recommendation,
});
break;
case "FLAGGED":
await alerts.warn(tpl.owner, "Negative feedback: may be disabled soon");
break;
default: // PENDING, IN_APPEAL, ARCHIVED, DELETED...
await audit.log("message_template_status_update", v);
}
}

Record the state, then release, hold, cancel or explain — and log anything else.

Common mistakes with the template status webhook

  • Launching on creation instead of approval

    A template returned by the create call is usually PENDING. Sending it fails. Schedule campaigns against the APPROVED webhook, not the create response.

  • Treating PAUSED and DISABLED alike

    Cancelling a campaign on a 3-hour pause wastes a working template; retrying a disabled one fails every time. Hold on PAUSED, replace on DISABLED.

  • Ignoring FLAGGED

    FLAGGED means negative feedback that may lead to a disable. It is the last cheap moment to cut frequency or tighten the audience for that template.

  • Assuming reason is always set

    reason is null for a template scheduled for deletion, and NONE on pauses. Code that switches on reason alone mislabels these events; route on event first.

Example: A festive template that launched itself, Jaipur

A Jaipur travel agency submits a Navratri package template at 6 pm, with a 10 am campaign scheduled for the next day. At 6:40 pm a REJECTED webhook arrives with reason: INVALID_FORMAT and a rejection_info.recommendation pointing out that the body starts with a variable. The editor shows the note, the marketer moves {{1}} after a greeting and resubmits. At 8:15 pm an APPROVED webhook releases the scheduled campaign, which goes out at 10 am as planned.

— A festive template that launched itself, Jaipur

Template Status Webhook FAQs

How long after submitting will I get the status webhook?+
Review is automatic and can take up to 24 hours, so the APPROVED or REJECTED webhook can arrive within minutes or the next day. Appeals are decided within 24 hours.
Why is reason NONE when my template was paused?+
reason describes review outcomes. Pause and unpause details are in other_info, whose title says whether it was a first pause, a second pause or a rate-limiting pause.
How is this different from the template quality webhook?+
The quality webhook reports score changes (GREEN, YELLOW, RED). This webhook reports state changes — including the pause that a RED score can cause.
Can I check a template's status without the webhook?+
Yes, by reading it from the API, but only as a backstop. A GET on the WABA's message_templates edge (or on one template ID) returns each template's status and category; use it to reconcile after downtime rather than polling every few minutes.

Why does the template status webhook matter?

Templates are the only way to start a conversation on WhatsApp, so a template's state decides whether a campaign or an automation can run at all. Reacting to this webhook lets a system launch a campaign the minute its template is approved, hold it during a pause instead of failing thousands of sends, switch to a backup when a template is disabled, and show authors exactly why Meta rejected their copy — without anyone polling the templates list.

Best practices for the template status webhook

  • Keep one approved backup template for every critical automation, such as OTPs and order updates.
  • Show the latest status and its reason next to each template in your editor, not just in logs.
  • Count rejections by reason per author to target copy guidelines where they help most.
  • Alert a person on DISABLED and FLAGGED; most other events can go straight to automation.

Talk to InfiQ

From definition to your first message

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.