Body
string
required
The customer’s number. E.164, with or without the
+; spaces are fine.Normalised the same way the contact importer does, so +91 98765 43210 and
919876543210 are the same customer rather than two.string
required
The name of an approved template on your account.
string[]
Values for
{{1}}, {{2}} … in the body, in order.string[]
The value for a link button’s blank. Separate on purpose — Meta wants it in a
different component, and sending it in
variables breaks both the words and
the link.string
The picture for a template whose header is an image, video or document.Required on every send for those templates. WhatsApp does not reuse the
file the template was approved with — that one exists for the review — so a
media template sent without a picture is refused with
409.Leave it out and the template’s own stored picture is used. Send one and it
wins, which is usually what you want here: the photograph on “your order has
shipped” is that customer’s parcel, not a stock image kept on the template.WhatsApp fetches it itself, so it has to be a public address.string
Which approved language version to send, exactly as approved —
en_US, hi.Leave it out and you get the oldest approved version, which is the one that
existed when you wrote the integration. That is deliberate: adding a language
in the console must never change what a working integration sends.Ask for one that is not approved and the 404 names the ones that are, in an
available array, so you can fix it from the response.object
Pre-fills a form the template opens. Only keys the form declared as
pre-fillable reach the screen; anything else is ignored rather than refused.
A template is identified by its name, not by an id — so the name is
stable across edits, and names are per account, which is why another
business’s template is invisible to you.
Response
messageRef is WhatsApp’s own receipt for the message. It is what delivery and
read reports are reported against, and what to quote if you ever have to ask
what happened to one.
A repeat of an idempotency key that already succeeded answers identically, with
one field added and a header alongside it:
{ "error": "…" } with the status from
Errors — and the sentence is meant to be read, not
matched on, because it names the specific thing that was wrong.
Idempotency
Send anIdempotency-Key header. A repeat of the same key sends nothing.
idempotencyKey works too; the header wins if you send both.
The claim is written before the send, which protects against the case that
actually happens: two retries arriving at once, rather than one after the other.
The second of the two gets 409 — that key is already being processed — and
not a second message. Retry after a moment and the answer will be waiting.
A refusal is remembered as well, and replayed as itself. If the first attempt
came back 409 template not approved, so does the retry, rather than sending
once the template is approved an hour later under a key you thought was spent.
Keys are honoured for 24 hours. That is Stripe’s window and long enough for
any retry worth making. After it, the same string is a new request and sends
again — so a key you reuse on a schedule, like
daily-digest-priya, will send
each day rather than replay for ever. That is usually what you want; it is
worth knowing either way.Worked example
Asking for something back
A template can carry a button that opens a form.formData fills it in
before the customer sees it: