Start here
Message templates and approval
Why business-initiated messages must be templates, how to submit them, and what each approval state means.
A template is a message Meta has read and approved before you send it. It has fixed wording and numbered gaps, {{1}}, {{2}}, that you fill at send time with an order number, a name, a tracking link.
The body can carry WhatsApp's own formatting, and it is the delivered message that matters rather than the source. *asterisks* around a word make it bold; the customer never sees the asterisks. Here is the approved order_shipped body as submitted:
🚚 Good news, your order *{{1}}* has shipped!
Tracking number: {{2}}
Track your shipment: {{3}}
It is on its way to you.🚚 Good news, your order #ORD-12345 has shipped!
Tracking number: AWB123456789
Track your shipment: https://example.com/track/AWB123456789
It is on its way to you.
You cannot write a business-initiated WhatsApp message freely. Meta reviews the wording in advance because the alternative is a channel where anyone with a phone number can send anything to anyone, and WhatsApp's value to its users depends on that not being true.
The three categories
Every template carries a category. It is not a label for your own filing. It decides what the template is allowed to say, what it costs, and what happens if a customer objects.
- UTILITY
A message about a transaction the customer has already entered into: an order confirmation, a status change, a shipping update, a refund, an invoice. Fifteen of the eighteen catalog events are utility.
Cheapest of the three, and the least likely to damage your quality rating, because it is what the customer is waiting for. It must genuinely relate to a real transaction, a "utility" template carrying a discount code is a marketing template that has not been caught yet.
- MARKETING
Anything promotional: cart recovery, review requests, back-in-stock alerts, offers, announcements. Three catalog events are marketing.
Several times the price of a utility message, and the category that moves your quality rating. Meta requires an opt-out affordance, so the catalog's marketing templates carry a Reply STOP to unsubscribe footer, and STOP is honoured automatically. Sending marketing to people who did not ask for it is the fastest way to a yellow rating and a cut messaging limit.
- AUTHENTICATION
One-time passcodes only: login codes, password resets, cash-on-delivery order confirmation codes. Three catalog events are authentication.
The shape is fixed by Meta, not by you. The body copy is generated by Meta with a security recommendation, the footer states the expiry, and there is a Copy code button. The code is passed twice on send, once for the body and once for the button, which the platform does for you. There is no custom wording to write, and none to get rejected.
Category decides the price
Meta prices a conversation by category and that ratio is passed through. Under the shipped default rate card a marketing message costs several times a utility one, and a free-form reply inside an open window costs nothing at all. Your plan can change every one of those numbers, so read the response headers rather than assuming a price, X-Message-Category and X-Message-Price are on every successful send. Credits and billing has the rest.
Submitting the catalog
The platform ships 18 ready-made templates covering the transactional events an online store needs. Submitting them is one action, from E-commerce Notifications in the dashboard, or one API call with an empty body:
curl -X POST https://whatsapp-api.growcord.in/api/v1/notifications/templates/submit \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{}'To submit a subset, name the events. onlyMissing defaults to true, which skips anything already approved or pending and re-submits anything rejected, so you can run it repeatedly without creating duplicates:
{
"events": ["order_confirmation", "order_shipped"],
"onlyMissing": true
}Submissions run one at a time because Meta rate-limits template creation per business account, so a full run of 18 takes several seconds. The response reports each one as submitted, already_exists or failed, with Meta's message on the failures.
Every approval state, and what to do about it
Approval is Meta's, and Meta is the source of truth for it. The platform mirrors the state locally so the happy path costs no external call, and re-reads from Meta whenever the answer would otherwise be "not approved" so a template approved a minute ago is picked up immediately rather than failing for another hour.
Read the current state per event, refreshing from Meta first:
| State | What it means | What to do |
|---|---|---|
| NOT_CREATED | Meta has never seen this template for this number. It was not part of a submission, or the submission failed. | Submit it. If it was in a run that reported failed, read Meta's message on that result first, usually a name clash with a template that already exists on the business account. |
| PENDING | Submitted, and Meta is reviewing. Authentication templates are usually approved almost immediately; the rest take minutes to a few hours. | Wait, and poll ?refresh=true. There is no way to hurry a review and re-submitting does not restart it, it creates a name clash. Sends for this event fail with 409 TEMPLATE_NOT_APPROVED until it lands. |
| APPROVED | Usable. This is the only state that can be delivered, and ready on the event is true only here. | Send it. Note that approval can be withdrawn later if the template performs badly, so a scheduled job should read readiness rather than cache it forever. |
| REJECTED | Meta refused it. The reason is on the event as statusReason, in Meta's own words. | Read statusReason, fix the cause, and re-submit, onlyMissing: true re-submits rejected templates, so a plain re-run is enough once the cause is addressed. |

Why a template gets rejected
Meta's reviewers are checking whether the message is what the category claims and whether a recipient would consider it wanted. The catalog templates are written to pass, so a rejection on one of them usually points at something about the account rather than the wording. Templates you write yourself fail for these reasons:
- The category does not match the content. The commonest rejection by a distance. A promotion submitted as
UTILITY, or a general update submitted asAUTHENTICATION. Meta either reclassifies it or refuses it. - Placeholders without examples. Meta requires an example value for every
{{n}}in a component, and the example count must match the placeholder count. - A template that is only placeholders. A body reading
{{1}} {{2}}tells a reviewer nothing about what will be sent, so it cannot be approved. - Placeholders in the wrong place. A body that starts or ends with a placeholder, or has two adjacent ones, is refused as unreviewable.
- A name that already exists. Template names are unique per business account and per language. This surfaces as a submission failure rather than a rejection.
- Content Meta will not carry, anything that reads as a scam, an unsolicited offer, or a policy-violating product.
Fix the cause and re-submit. A template rejected twice for the same reason will be rejected a third time; the reason in statusReason is the thing to act on, not the number of attempts.
Writing your own templates
The catalog covers transactional e-commerce. For anything else, submit your own with POST /v1/templates, and send it through POST /v1/messages/send with type: "template" and hand-built components.
{
"phoneNumberId": "1030974986765331",
"name": "welcome_message",
"language": "en_US",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, welcome to {{2}}!",
"example": { "body_text": [["Alex", "Acme"]] }
},
{ "type": "FOOTER", "text": "Reply STOP to unsubscribe" }
]
}Two rules that reject a submission before it reaches Meta or immediately after:
- The name must be lowercase
snake_case,^[a-z0-9_]+$. Uppercase, spaces and hyphens are refused. - Every component containing
{{n}}needs anexampleblock whose array length matches the placeholder count.
Note that a template Meta refuses comes back as 400 with Template submission failed: …, not 502, the request reached Meta and Meta answered.
What happens at send time
When you send an event, the platform looks for an approved template of that name on that number, in the language you asked for, en_US also matches a template approved as en. If it does not find one it re-reads from Meta once, in case approval landed since the last sync, and only then gives up:
{
"error": "No approved WhatsApp template named \"order_shipped\" for this number. Submit it for verification (POST /v1/notifications/templates/submit) and wait for Meta approval, business-initiated messages cannot be sent as plain text.",
"code": "TEMPLATE_NOT_APPROVED",
"details": { "event": "order_shipped", "templateName": "order_shipped", "language": "en_US" }
}That 409 is a stated failure rather than a silent non-delivery, which is the point: you find out at the moment of sending, not from a customer three days later. Do not retry it, poll readiness instead. Troubleshooting covers what to do when it appears in production.
