Skip to content
Linq Copy agent prompt
Templates

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.

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_schema appears up to about fifteen minutes after approval; until then a send is refused.
  • category is the one review assigned, which can differ from what you submitted. It sets pricing and whether marketing rules apply.
  • A draft may be resubmitted only when retry_allowed is true.
  • New status values may appear. Treat any you do not recognise as not sendable.
  • rejected_reason and quality are free text. Never branch on their wording.
{
"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 name and language pair already registered beyond draft answers 409. 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.

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: absolute https, up to 2,000. Only the final {{1}} may vary. Percent-encode the value yourself.
  • type defaults to url.

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.

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.

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.

{
"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.

{
"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 url slot is required. Its payload replaces {{1}}.
  • A quick_reply slot is optional. Omit it and a tap returns the button’s label.
  • Fixed buttons have no slot. Payloads are up to 256.

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.

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.

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, not text.

The same part answers reply buttons on an interactive_carousel.

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.