WhatsApp permits free-form messages only within 24 hours of the customer’s last
inbound message. Outside it — a broadcast, an order update, a payment reminder,
anyone who has never written to the merchant — only an approved template may
be sent.
So a partner running campaigns needs to create and track templates without a
merchant logging into anything.
Reading takes channels:read; creating and deleting take channels:write.
Listing
Returns Meta’s own payload — data[] with each template’s name, status,
category, language, components and, when it was refused,
rejected_reason.
Pass phone_number_id when a merchant holds numbers on more than one WhatsApp
Business Account: templates belong to the WABA, not to the merchant.
Creating
UTILITY, MARKETING or AUTHENTICATION. This is what you are billed on,
and Meta re-categorises templates it thinks are miscategorised — a marketing
message dressed as a utility one gets moved, and priced accordingly.
Positional placeholders, {{1}} upward. Every one of them needs a matching
entry in example_body_params or Meta rejects the template outright.
201 with Meta’s response, including the template id and its initial status —
almost always PENDING.
A template Meta refuses at submission comes back 400 carrying their
validation error verbatim rather than a flattened one, because their message
names the component that was wrong.
Approval is asynchronous, and slow
A 201 does not mean you can send it. Review takes minutes at best and hours
routinely. A campaign scheduled against a template created moments earlier will
fail at send time with a channel_rejected naming the template.
The outcome arrives as a webhook:
status is APPROVED, REJECTED, PAUSED or DISABLED. A template can move
after approval: Meta pauses one whose recipients repeatedly block or report
the sender, and disables it if that continues. Treat PAUSED as a signal about
the message, not a glitch — it means people did not want it.
Store the status against your own copy and check it before scheduling, rather
than listing templates on every send.
Sending one
Templates are sent through the ordinary message routes, addressed either to a
thread or straight to a number:
Positional, in {{1}}, {{2}} order. See
Conversations and
messaging a number with no thread.
Deleting
Meta deletes by name, which removes every language version of it. Anything
still scheduled against that template starts failing at send time rather than
here, so delete deliberately.
What to build
Mirror the status, do not poll for it
Store each template’s status locally and update it from
template.status_changed. Listing templates before every send costs a Graph
call per broadcast and tells you nothing new between approvals.
UTILITY is cheaper than MARKETING, which is why every platform sees
merchants labelling promotions as order updates. Meta re-categorises, bills the
corrected category, and a merchant whose templates keep being moved attracts
quality review. Label it as what it is.
Expect the window, design around it
A merchant’s first contact with a customer is always a template. Free-form
replies only become available once the customer answers — which is what the
24-hour window in every conversation payload tells you.