Templates
Register and send reviewed templates outside the customer window.
A template is reviewed copy you register once and send many times. It is the
only message you can send outside the 24-hour customer window. Register with
POST /v1/templates, read status and send shape with
GET /v1/templates, and send it as a template part.
Every other part type is on Message types.
| Step | Call | What to read |
|---|---|---|
| Register | POST /v1/templates |
status (almost always pending) |
| Wait | GET /v1/templates |
status until approved; category |
| Compose | GET /v1/templates |
parameters and send_schema |
| Send | POST /v1/messages |
202 accepted; outcome on events |
| Reply | inbound template_reply part |
payload of the tapped quick-reply button |
For WhatsApp’s template categories, review rules, and lifecycle, see the WhatsApp Business Platform message template documentation. Linq’s template endpoints are the authority for the request and response shapes you use with this API.
Status
Section titled “Status”Only an approved template can be sent. Approval is not permanent: a template
may later be paused for quality and become approved again. Read the registry
rather than caching approval forever.
status |
Meaning | Sendable |
|---|---|---|
draft |
A submission attempt has not produced a registered template. | No |
pending |
Under review. | No |
approved |
Sendable once send_schema is published. |
Yes |
rejected |
Refused. rejected_reason may say why. |
No |
paused |
Held, usually for quality. Can return to approved on its own. |
No |
disabled |
No longer usable. | No |
- Review takes minutes to several days. Poll the list rather than assuming a wait.
send_schemaappears up to about fifteen minutes after approval; until then a send is refused.categoryis the one review assigned, which can differ from what you submitted. It sets pricing and whether marketing rules apply.- A
draftmay be resubmitted only whenretry_allowedistrue. - New status values may appear. Treat any you do not recognise as not sendable.
rejected_reasonandqualityare free text. Never branch on their wording.
Register a template
Section titled “Register a template”{ "name": "order_shipped", "language": "en_US", "category": "utility", "body": { "text": "Your order {{order_id}} is on its way.", "examples": { "order_id": "A-1029" } }}| Field | Limits |
|---|---|
name |
Lowercase letters, digits, underscores; up to 512. Permanent once submitted. |
language |
A locale such as en_US, up to 32. Each language is registered separately. |
category |
utility, marketing or authentication. |
body.text |
Up to 1,024. Variables are {{named}}; numbered {{1}} is refused. At most 32. |
body.examples |
One per variable, required. 1–1,024 characters; no line break, tab or four spaces. |
footer |
Up to 60, no variables. |
buttons |
Up to 10: at most two url, quick replies kept together. Label up to 25. |
media_header |
image, video or document by upload_ref. JPEG, PNG, MP4 or PDF, up to 5 MiB. |
- A
nameandlanguagepair already registered beyonddraftanswers409. Pick another name, or register a new language under it. - A request refused before submission records nothing, and the name stays reusable.
- An unknown submission outcome (2039) is permanent: do not resubmit that name.
Buttons
Section titled “Buttons”A url button opens an https address baked into the template. It may end in
{{1}}, filled at send, and then needs an example. A quick_reply button
returns a payload you set at send.
{ "name": "order_tracking", "language": "en_US", "category": "utility", "body": { "text": "Order {{order_id}} has shipped.", "examples": { "order_id": "A-1029" } }, "buttons": [ { "type": "url", "text": "Track order", "url": "https://example.com/orders/{{1}}", "example": "A-1029" }, { "type": "quick_reply", "text": "Talk to us" } ]}url: absolutehttps, up to 2,000. Only the final{{1}}may vary. Percent-encode the value yourself.typedefaults tourl.
Authentication templates
Section titled “Authentication templates”An authentication template has fixed, localized copy, so omit body and
send authentication instead.
{ "name": "login_code", "language": "en_US", "category": "authentication", "authentication": { "otp_type": "copy_code", "code_expiration_minutes": 10 }}| Field | Limits |
|---|---|
otp_type |
copy_code, one_tap or zero_tap. Required. |
package_name, signature_hash |
Required for one_tap and zero_tap. |
zero_tap_terms_accepted |
Must be true for zero_tap. |
code_expiration_minutes |
1 to 90. |
message_send_ttl_seconds |
30 to 900. |
copy_code_text, autofill_text |
Up to 25. |
add_security_recommendation |
Boolean. |
An authentication template cannot carry a carousel or media_header.
Carousel templates
Section titled “Carousel templates”A carousel holds 2 to 10 cards, each with image or video media, an
optional body and up to two buttons. Every card uses the same media kind,
body presence and button order. A carousel cannot be combined with footer,
media_header or buttons, and needs category marketing or utility.
Card media is named by an asset_handle from the channel’s template upload,
not an upload_ref.
Read the send shape
Section titled “Read the send shape”GET /v1/templates lists templates in every status,
newest first. Filter on status == "approved". Pages hold up to 100
(limit); while has_more is true, pass next_cursor as cursor for the
next page.
| Field | Use |
|---|---|
parameters |
JSON Schema for the named variables. Empty properties: no variables. null: cannot be sent by name. |
send_schema |
The complete schema for this template’s template part, including header, buttons and cards. null until published. |
content |
The copy and fixed buttons, for a human preview. Never rebuild copy from send_schema. |
Hand parameters to a model verbatim as a tool definition. Send header
only when send_schema requires that exact media kind.
Send a template
Section titled “Send a template”{ "to": "15550101234", "parts": [ { "type": "template", "name": "order_shipped", "language": "en_US", "parameters": { "order_id": "A-1042" } } ]}| Field | Use |
|---|---|
name, language |
Identify the exact approved template on the sending account. |
parameters |
Named values. Omit for a template with no variables. |
header |
kind and upload_ref, only when send_schema requires a media header. |
buttons |
index, type, payload per dynamic button, in index order. |
cards |
Per-card values for a carousel template: card_index, kind, upload_ref, parameters, buttons. |
Each parameter value is 1 to 1,024 characters, not whitespace-only, with no line break, tab or four consecutive spaces.
Button values
Section titled “Button values”{ "to": "15550101234", "parts": [ { "type": "template", "name": "order_tracking", "language": "en_US", "parameters": { "order_id": "A-1042" }, "buttons": [ { "index": 0, "type": "url", "payload": "A-1042" }, { "index": 1, "type": "quick_reply", "payload": "talk-A-1042" } ] } ]}- A
urlslot is required. Its payload replaces{{1}}. - A
quick_replyslot is optional. Omit it and a tap returns the button’s label. - Fixed buttons have no slot. Payloads are up to 256.
Authentication codes
Section titled “Authentication codes”An authentication template takes exactly one parameter, code: the one-time
code you generate, 1 to 15 characters. The message shows it in its body and
behind its code button.
{ "to": "15550101234", "parts": [ { "type": "template", "name": "login_code", "language": "en_US", "parameters": { "code": "482913" } } ]}- A missing
code, or any other parameter, is refused with 4007. - A code over 15 characters is refused, never shortened.
An authentication template with a one-time-code button (copy code, one-tap,
or zero-tap autofill) needs the customer’s phone number. Sent to a customer
known only by a
username id,
it cannot be delivered. When the stored template already identifies the
one-time-code button, the send is refused with HTTP 422 and error.code
4011, and nothing is sent. If the
refusal can only be known after acceptance, the message fails with
status: failed and failure.code: recipient_phone_number_required instead.
The customer window
Section titled “The customer window”Outside the 24-hour customer window only an approved template sends; any
other part is refused with 2036.
Inside the window a template is an ordinary send. Templates skip only the
window check: validation, spend limits and suppressions still apply. See
Sending messages.
Quick-reply taps
Section titled “Quick-reply taps”A tap on a quick-reply button arrives as an inbound template_reply part. The
message’s reply_to_message_id names the template message that carried the
button.
{ "type": "template_reply", "payload": "talk-A-1042", "text": "Talk to us" }payload: the slot’s payload from your send, or the label if you left the slot out. Up to 256.text: the label the customer saw. Up to 25.- Branch on
payload, nottext.
The same part answers reply buttons on an
interactive_carousel.
Refusals
Section titled “Refusals”A template send is validated against the registry before acceptance. A missing
or non-approved template, wrong language, or mismatched parameter set is
refused with HTTP 422 and error.code
4007. Linq never substitutes a
different template. The detail points at the field to fix, such as
/parts/0/name, /parts/0/language (listing the registered languages) or
/parts/0/parameters/order_id.
| Code | When |
|---|---|
| 1017 | Registration: the definition failed validation. |
| 1024 | Registration: the channel refused the template. |
| 2038 | Registration: the name and language already exist. |
| 2039 | Registration: outcome unknown. Do not resubmit the name. |
| 3011 | Registration: the media header could not be prepared. |
| 3021 | Registration: media still preparing. Retry the same body. |
| 3012 | The template channel did not answer. |
| 2034 | The account has no channel account yet. |
| 4007 | Send: the template cannot be sent as addressed. |
| 4011 | Send: one-time code to a customer without a phone number. |
| 2036 | Send: a non-template part outside the window. |
| 4006 | Send: a spend cap. At a daily cap, template sends are refused. |