On this page
- 1. Variable and example mismatch
- 2. Promotional content in a Utility template
- 3. Formatting violations
- 4. Prohibited categories
- 5. Link shorteners
- 6. Missing opt-out context in Marketing templates
- 7. Template name policy
- 8. Spammy punctuation and formatting
- 9. Non-sequential variables
- The summary table: reason to fix
- The resubmission workflow
- Versioning discipline: the _v2 habit
- Getting ahead of rejections entirely
Template rejections are the single most common support ticket we see from teams new to the WhatsApp Business API. Not delivery issues, not webhook bugs — rejections. A team writes what looks like a perfectly reasonable message, submits it, waits a few hours, and gets back a one-line verdict: REJECTED. The reason field, when it's populated at all, says something terse like INVALID_FORMAT or TAG_CONTENT_MISMATCH, and the team is left guessing.
The good news: rejections are boringly predictable. After reviewing hundreds of rejected templates across accounts — D2C brands in Bengaluru, clinics in Pune, NBFCs, tuition centres, everyone — the same nine reasons account for the overwhelming majority of failures. Most reviews resolve within minutes to a few hours, and a template rewritten against this list typically passes on the first resubmission.
This post walks through each of the nine, with a rejected version and an approved rewrite side by side, then covers the resubmission workflow and the versioning discipline that keeps your template library from turning into a graveyard of promo_final_final_2 entries.
1. Variable and example mismatch
Every variable in a template body needs a sample value at submission time, and the sample has to make sense. The review system checks that examples are plausible, that they don't themselves contain policy violations, and that the message reads coherently when the samples are substituted in. The two failure modes we see constantly: missing examples entirely, and lazy examples like "test" or "123" for every placeholder.
REJECTED — order_update
Body: Hi {{1}}, your order {{2}} has shipped and will arrive by {{3}}.
Examples: "1", "2", "3"
The reviewer substitutes those in and reads: "Hi 1, your order 2 has shipped and will arrive by 3." That's not a message a human would send, so it fails.
APPROVED — order_update_v2
Body: Hi {{1}}, your order {{2}} has shipped and will arrive by {{3}}.
Examples: "Priya", "ORD-48291", "Thursday, 12 June"
Same body, realistic examples, passes. Run drafts through the template variable checker before submitting — it flags missing examples and placeholder-shaped junk values.
2. Promotional content in a Utility template
This is the expensive one, because Utility conversations are priced lower than Marketing conversations, and the categorisation system knows teams have an incentive to shade promotional content into the cheaper category. Since Meta moved to automated category enforcement, templates get recategorised or rejected when the content doesn't match the declared category — and appeals on this ground rarely succeed.
The rule of thumb: a Utility template facilitates a specific, agreed-upon transaction or updates the customer about it. The moment you add "check out our new collection" to a delivery confirmation, you've written a Marketing template.
REJECTED — Utility category, delivery_confirm
Body: Hi {{1}}, your order {{2}} was delivered today. While you're here,
don't miss our monsoon sale — up to 40% off across the store!
APPROVED — Utility category, delivery_confirm_v2
Body: Hi {{1}}, your order {{2}} was delivered today. Reply here if
anything is missing or damaged and we'll sort it out.
If you genuinely want the sale message, submit it separately as Marketing and pay the Marketing rate. Mixing the two gets you the worse of both worlds: a rejection now and closer scrutiny on future submissions. We see this pattern across accounts — a Tier 2 electronics retailer we work with had eleven consecutive Utility rejections before splitting transactional and promotional flows into separate templates, after which their approval rate went to effectively 100%.
3. Formatting violations
Mechanical rules, mechanically enforced. The common ones: variables at the very start or very end of the body, two variables directly adjacent with no text between them, more than one consecutive newline in some placements, and body text that is disproportionately variables (a body that's mostly placeholders reads as spam-shaped).
REJECTED — payment_reminder
Body: {{1}}{{2}} is due. Pay: {{3}}
Three violations in one line: opens with a variable, has adjacent variables, and ends with one.
APPROVED — payment_reminder_v2
Body: Hi {{1}}, your payment of {{2}} for invoice {{3}} is due on {{4}}.
You can pay using the button below. Ignore this if you've already paid.
Variables cushioned by text on both sides, body opens and closes with words. The JSON template validator catches all of these structurally before Meta ever sees the payload.
4. Prohibited categories
Some businesses and products can't be promoted on the platform at all, regardless of how the template is worded: gambling and real-money gaming, alcohol, tobacco, weapons, adult content, cryptocurrency trading, and — the one that surprises Indian teams most often — certain lending and loan products, which face restrictions that vary and get enforced unevenly. Supplements and some healthcare claims sit in a grey zone where phrasing determines the outcome.
REJECTED — health_promo
Body: Hi {{1}}! Our new fat-burner capsules guarantee 5kg weight loss
in 30 days. Order now and transform your body!
Guaranteed health outcomes are a hard no. But an operational message from the same business is fine:
APPROVED — order_ready_v2
Body: Hi {{1}}, your order {{2}} from NutriStore is packed and ready
for dispatch. Expected delivery: {{3}}.
If your industry is anywhere near a restricted vertical — pharma, wellness, financial services — keep templates strictly operational: confirmations, reminders, status updates. Save the persuasion for channels where the policy allows it.
5. Link shorteners
Shortened URLs — bit.ly, tinyurl, cutt.ly, and friends — hide the destination from both the reviewer and the recipient, so they're treated as a spam signal and rejected outright. This also applies to generic redirect domains and, in practice, to tracking domains that don't obviously belong to your business.
REJECTED — feedback_request
Body: Hi {{1}}, thanks for shopping with us! Share your feedback here:
bit.ly/3xKp9dQ
APPROVED — feedback_request_v2
Body: Hi {{1}}, thanks for shopping with us! Share your feedback here:
https://feedback.meerastores.in/survey
Use a full URL on a domain the business owns. If you need click tracking, put UTM parameters on your own domain or use a branded subdomain (go.yourbrand.in) — reviewers accept those because the destination is transparent. A URL inside a variable also draws scrutiny: if you must parameterise a link, keep the domain in the static text and put only the path or token in the variable.
6. Missing opt-out context in Marketing templates
Marketing templates fare noticeably better in review — and dramatically better with recipients — when they acknowledge the customer's control over the channel. An unsubscribe hint isn't a hard requirement in every region, but templates without any opt-out affordance get rejected often enough, and get marked spam by users so much more often, that we treat it as mandatory. High block rates feed back into your quality rating, and a falling quality rating throttles your messaging limits. It compounds.
REJECTED — festive_offer
Body: {{1}}, Diwali Dhamaka is LIVE! Flat 50% off everything.
Shop before midnight!
APPROVED — festive_offer_v2
Body: Hi {{1}}, our Diwali sale is live — flat 50% off sitewide until
{{2}}. Tap below to browse. Reply STOP to opt out of offers.
Buttons: [Shop now] [Stop promotions]
The quick-reply opt-out button is the cleanest pattern: it satisfies the review, and it routes unhappy recipients to a silent unsubscribe instead of the block button. Across accounts, adding it typically cuts block rates meaningfully — we generally see marketing lists hold their quality rating far longer once it's standard.
7. Template name policy
Template names must be lowercase, alphanumeric, and underscore-separated. No spaces, no hyphens, no capitals, no emoji. Beyond the mechanical rule, names that look like keyword-stuffing or that misrepresent the content ("account_alert" on a discount blast) attract rejections and, worse, attract manual review of your whole library.
REJECTED
Name: "Diwali OFFER!! 50% Off"
APPROVED
Name: diwali_offer_2026_v1
Adopt a naming convention early: purpose_context_version, e.g. order_shipped_cod_v1, payment_reminder_emi_v2. Six months in, when you have 80+ templates across three languages, you'll be glad the names sort meaningfully.
8. Spammy punctuation and formatting
ALL CAPS, repeated exclamation marks, emoji walls, and s p a c e d o u t letters all pattern-match to spam. One emoji is fine. "SALE!!!" is not. The classifier here is blunt, and it errs toward rejection.
REJECTED — flash_sale
Body: 🔥🔥🔥 MEGA SALE {{1}} 🔥🔥🔥 HURRY!!! ONLY TODAY!!!
GRAB IT NOW!!! 💸💸💸
APPROVED — flash_sale_v2
Body: Hi {{1}}, our 24-hour flash sale ends tonight at 11:59 PM.
Extra 20% off with code {{2}}. 🔥 Reply STOP to opt out.
Notice the approved version still has urgency and still has an emoji — it just reads like a message from a business, not a scam forward from a family group. The urgency lives in the facts (a real deadline, a real code), not the punctuation.
9. Non-sequential variables
Variables must be numbered sequentially starting from {{1}} with no gaps. {{1}}, {{2}}, {{4}} fails. {{2}}, {{3}} without a {{1}} fails. This one bites teams who edit a template draft, delete a middle variable, and forget to renumber — it's probably the most common purely mechanical rejection we see.
REJECTED — appointment_reminder
Body: Hi {{1}}, reminder: your appointment with Dr. {{3}} is on {{4}}
at {{5}}. Reply 1 to confirm.
APPROVED — appointment_reminder_v2
Body: Hi {{1}}, reminder: your appointment with Dr. {{2}} is on {{3}}
at {{4}}. Reply 1 to confirm.
This is exactly the class of error tooling should catch, not reviewers. The template variable checker flags gaps and duplicates instantly, and the JSON template validator verifies the full component payload — header, body, footer, buttons — against the schema before submission.
The summary table: reason to fix
| # | Rejection reason | Typical error signal | The fix |
|---|---|---|---|
| 1 | Variable/example mismatch | INVALID_FORMAT |
Realistic sample values for every variable |
| 2 | Promo content in Utility | Recategorised or rejected | Split transactional and promotional into separate templates |
| 3 | Formatting violations | INVALID_FORMAT |
No leading/trailing/adjacent variables; text cushions each placeholder |
| 4 | Prohibited categories | ABUSIVE_CONTENT / policy |
Keep restricted verticals strictly operational |
| 5 | Link shorteners | Policy rejection | Full URLs on owned domains; UTM instead of bit.ly |
| 6 | Missing opt-out context | Rejection or quality decay | Add "Reply STOP" line or an opt-out quick-reply button |
| 7 | Name policy | Submission error | lowercase_underscore_names, honest purpose |
| 8 | Spammy punctuation | ABUSIVE_CONTENT |
One emoji max, no caps-lock, urgency via facts |
| 9 | Non-sequential variables | INVALID_FORMAT |
Renumber from {{1}} with no gaps |
The resubmission workflow
When a rejection lands, resist the urge to immediately tweak one word and resubmit. Repeated rapid rejections on near-identical content can draw wider scrutiny to the account. Work the problem instead:
- Read the rejection reason code, even when it's vague.
INVALID_FORMATpoints at reasons 1, 3, and 9 — mechanical issues you can validate locally. Policy-flavoured rejections point at 2, 4, 5, 6, and 8 — content issues that need a rewrite, not a patch. - Diagnose against the table above. In our experience a rejected template usually violates two or three rules at once, not one. The caps-lock template also has the bit.ly link and no opt-out. Fix everything in one pass.
- Validate locally. Run the corrected payload through the JSON template validator and the template variable checker. This catches every mechanical failure class before Meta sees it, which keeps your rejection history clean.
- Question the category before resubmitting. If the honest answer is "this is a marketing message," submit it as Marketing. A Marketing template that delivers beats a Utility template that never gets approved.
- Submit as a new version — never edit-and-pray on the same name. More on this below.
- Appeal only when you're right. If you genuinely believe a rejection is a false positive — it happens, particularly with regional-language templates where the classifier is weaker — use the appeal option in the Business Manager rather than resubmitting clones. Appeals on clear-cut policy grounds (categories 2, 4, 5) are usually a waste of a few days; appeals on formatting false positives succeed reasonably often.
Review times vary: many templates clear within minutes via automated review, while anything that trips a manual check can take up to 24–48 hours. Build that lag into campaign planning — submitting the Diwali template on Dhanteras morning is how teams end up sending nothing at all.
Versioning discipline: the _v2 habit
Every template name should end in a version suffix from day one: order_shipped_v1, not order_shipped. When a template is rejected, or when an approved template needs a copy change, you create order_shipped_v2 and submit that. The old version stays untouched until the new one is approved and deployed, then gets retired deliberately.
Why this matters more than it looks:
- Zero downtime. Editing an approved template in place puts it back into review — and your integration keeps firing sends against a template that's suddenly in
PENDINGstate. With versioning,_v1keeps delivering while_v2sits in review. - A clean audit trail. When a client asks "why did the reminder copy change in April," the answer is in the version history, not in someone's memory.
- Safer rollbacks. If
_v2's copy tanks read rates, you point the integration back at_v1in one config change. No re-review, no waiting. - Cleaner analytics. Delivery and read metrics stay attributable to a specific piece of copy instead of blurring across silent edits.
The operational pattern we recommend: keep template names in config, not hardcoded in application code, so a version bump is a config deploy rather than a release. Retire old versions on a schedule — a quarterly cleanup that deletes anything two versions stale keeps the library navigable. And log every rejection with its reason in a shared sheet or ticket; after a quarter you'll have your own account-specific version of this field guide.
Getting ahead of rejections entirely
The teams with near-zero rejection rates aren't lucky; they've moved the review left. A pre-submission checklist takes about two minutes per template:
- Category honestly matches content (the single highest-leverage check)
- Every variable numbered sequentially, cushioned by text, with a realistic example
- Full URLs on owned domains, no shorteners
- Marketing templates carry an opt-out line or button
- Name is lowercase_underscore with a version suffix
- One emoji maximum, no caps-lock, no exclamation chains
- Payload passes the JSON template validator
Rejections will still happen occasionally — regional-language false positives, policy edge cases, the odd inexplicable verdict that clears on appeal. But they become a monthly curiosity instead of a weekly blocker, and your campaign calendar stops depending on review-queue roulette.
If you'd rather see the whole workflow — drafting, validation, submission, version tracking — inside one dashboard, InfiQ (a Meta Business Partner) bundles all of it, and the 7-day free trial is enough time to migrate and clean up an existing template library end to end.

