---
title: Templates | API Docs
description: 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](/channel/whatsapp/guides/messaging/message-types/index.md).

| 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](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/). Linq’s template endpoints are the authority for the request and response shapes you use with this API.

## 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_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.

## 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 `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](/channel/whatsapp/error/codes/2xxx/2039/index.md)) is permanent: do not resubmit that name.

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

### 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

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

`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

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

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

### 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](/channel/whatsapp/error/codes/4xxx/4007/index.md).
- 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](/channel/whatsapp/guides/messaging/sending-messages#customers-who-use-a-username/index.md), 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](/channel/whatsapp/error/codes/4xxx/4011/index.md), 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

Outside the 24-hour customer window only an approved `template` sends; any other part is refused with [2036](/channel/whatsapp/error/codes/2xxx/2036/index.md). 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](/channel/whatsapp/guides/messaging/sending-messages#choose-content-from-the-customer-window/index.md).

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

The same part answers reply buttons on an [`interactive_carousel`](/channel/whatsapp/guides/messaging/message-types#interactive_carousel/index.md).

## 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](/channel/whatsapp/error/codes/4xxx/4007/index.md). 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](/channel/whatsapp/error/codes/1xxx/1017/index.md) | Registration: the definition failed validation.                |
| [1024](/channel/whatsapp/error/codes/1xxx/1024/index.md) | Registration: the channel refused the template.                |
| [2038](/channel/whatsapp/error/codes/2xxx/2038/index.md) | Registration: the name and language already exist.             |
| [2039](/channel/whatsapp/error/codes/2xxx/2039/index.md) | Registration: outcome unknown. Do not resubmit the name.       |
| [3011](/channel/whatsapp/error/codes/3xxx/3011/index.md) | Registration: the media header could not be prepared.          |
| [3021](/channel/whatsapp/error/codes/3xxx/3021/index.md) | Registration: media still preparing. Retry the same body.      |
| [3012](/channel/whatsapp/error/codes/3xxx/3012/index.md) | The template channel did not answer.                           |
| [2034](/channel/whatsapp/error/codes/2xxx/2034/index.md) | The account has no channel account yet.                        |
| [4007](/channel/whatsapp/error/codes/4xxx/4007/index.md) | Send: the template cannot be sent as addressed.                |
| [4011](/channel/whatsapp/error/codes/4xxx/4011/index.md) | Send: one-time code to a customer without a phone number.      |
| [2036](/channel/whatsapp/error/codes/2xxx/2036/index.md) | Send: a non-template part outside the window.                  |
| [4006](/channel/whatsapp/error/codes/4xxx/4006/index.md) | Send: a spend cap. At a daily cap, template sends are refused. |
