# Authoring WhatsApp templates

Bird's managed catalog covers the common cases, but a template that speaks in your own words has to be authored on a WhatsApp Business Account you have connected. This page covers creating one; [WhatsApp templates](/docs/guides/whatsapp/templates) covers browsing and sending what already exists.

Three things shape the whole flow:

- **A template holds versions, and a version holds one entry per language.** What actually sends is a language of a version, not the template.
- **Content is written to a draft.** A template has at most one open draft, and nothing in it reaches WhatsApp until you submit it.
- **Approval comes back per language.** One language can be approved while another on the same version is rejected.

## Before you start

You need a [number of your own](/docs/guides/whatsapp/phone-number-setup) connected, which is what gives your workspace a WhatsApp Business Account to author on. An account you have not connected is refused, and so is editing one of Bird's built-in `bird_` templates: those live on Bird's own account, so duplicate one onto yours instead.

Authoring an **authentication** template additionally needs a verified business; utility and marketing do not. See [Authentication templates](/docs/guides/whatsapp/templates/authentication#before-you-send) for that gate.

Author a template in the dashboard under **WhatsApp** > **Templates**, with the [`bird` CLI](/docs/cli), or through the [MCP server](/docs/ai/mcp-server). The examples below use the CLI.

## Two ways to start

### Duplicate an existing template

The fastest start is to copy a template that already says nearly what you need, including one of Bird's. A duplicate lands on the account you name, carries the source's content as an open draft, and calls WhatsApp zero times, so nothing is submitted until you choose to.

Two things about a copy are worth knowing before you make one:

- **The category is inherited and cannot be changed.** If you need a different category, start from scratch instead.
- **You can narrow the languages, never add to them.** A catalog template stocking 70 languages does not have to become 70 languages of yours: pick the subset you will actually maintain. Asking for a language the source does not carry is refused with [`E15060`](/docs/api/errors/E15060), and the response names which ones did not match. Add further languages to the copy afterwards.

The language subset is an array, so it goes in a request body rather than a flag:

```bash
bird whatsapp templates duplicate bird_order_confirmation --body-file copy.json
```

```json
{
  "waba": "102290129340398",
  "slug": "acme_order_update",
  "include_languages": ["en", "es-ES"],
  "default_language": "en"
}
```

Omit `include_languages` and the copy takes all of the source's languages. Omit `default_language` and the copy keeps the source's default when your subset still carries it; otherwise it takes the first of the copy's languages by canonical tag, which is not necessarily the first one you listed, so set it explicitly if it matters.

### Start from scratch

Creating a template takes a slug, an account, a category, and a default language:

```bash
bird whatsapp templates create order_update \
  --waba 102290129340398 \
  --category utility \
  --default-language en
```

**The slug and the category are both permanent.** WhatsApp derives its own template name from the slug, and neither it nor the category can be changed later; a different one means a new template. The `bird_` prefix is reserved for Bird's catalog. The category you choose is not necessarily what a send is priced at: Meta applies its own category per language and can move one, and the price follows Meta's.

## Writing each language

Open the draft, then write one language at a time:

```bash
bird whatsapp templates versions create order_update
bird whatsapp templates versions languages set order_update <version-id> en --body-file en.json
```

Opening a draft is safe to repeat: a template has only one, so this returns the open one rather than creating a second. The template also reports it as `draft_version_id`.

**A language write replaces the language, it does not merge into it.** The file carries that language's complete `components` every time, so read the language first and write it back whole; sending only the block you changed deletes the rest.

**Every variable needs an example value.** WhatsApp reviews the filled-in message rather than the template, so a block with placeholders and no example parameters is refused at submit rather than at write time.

## Check, then submit

Validate before you freeze anything. A validate-only submit runs every check across every language and reports each problem in one pass, sending nothing to WhatsApp:

```bash
bird whatsapp templates versions submit order_update <version-id> --validate-only
```

Read `valid` and `errors`; each error names the language, the field, and the code a real submit would fail with. Then submit for real by dropping the flag. That freezes the draft as an immutable version and answers `202`. Use a different idempotency key for the check and the submit, since reusing one key with a changed body is rejected.

Only languages whose content differs from their approved copy go to WhatsApp. One that already matches carries its approval forward, so a submit where nothing changed settles immediately with nothing to poll. No replacement draft opens afterwards: the next round of edits starts by creating a draft again.

A clean validate-only run does not predict WhatsApp's decision. WhatsApp offers no way to ask in advance, so it can still refuse content that passed every local check.

## Watching the review

Approval arrives later and per language. The template's `pending_version_id` stays set while any language is unresolved, and the per-language list carries each verdict:

- **`approved`** sends. `available_languages` on the template is exactly what a send can resolve right now.
- **`rejected`, `submit_failed`, `paused`** need an edit on a fresh draft. WhatsApp accepts an edit to a paused language, and resubmitting is what clears it.
- **`disabled`, `limit_exceeded`, `in_appeal`** refuse an edit outright; they only need re-reading until WhatsApp moves them.

The template's own `status` is an aggregate: `active` means at least one language is sendable, not all of them.

## Sending what you authored

An authored template sends through the same endpoint as any other, with one difference from Bird's catalog: **you must name `from`**, and it has to be a number on the same WhatsApp Business Account as the template. A sender on a different account is refused `422` [`E15023`](/docs/api/errors/E15023) before anything is charged.

```json
{
  "to": "+16505551234",
  "from": "+13124495648",
  "template": {
    "slug": "order_update",
    "language": "en",
    "components": [
      {
        "type": "body",
        "parameters": [{ "type": "text", "name": "order_id", "text": "A1B2C3" }]
      }
    ]
  }
}
```

Naming a language is optional and resolves to the template's default, unless the template requires one explicitly. The values you supply have to fill the placeholders of whichever language actually resolves, so read that language's content before sending. See [How sending works](/docs/guides/whatsapp/sending-whatsapp) for the full payload.

## Things to watch

- **The newest version is not the one that sends.** A version list is newest-first and includes any open draft, so the top row is often a draft or a version still under review. The template names the version in service as `live_version_id`; a template with no live version cannot be sent at all.
- **A language under review refuses a write.** WhatsApp holds it until the review finishes, so an edit during `pending` fails rather than queueing.
- **A list row carries no content.** Listing templates finds them and shows lifecycle state; reading what one actually says takes a version read.
- **Deleting is unrecoverable.** Discarding a language, deleting a draft, and deleting a template all take an explicit confirmation, and deleting a template stops every send by that slug.

## Next steps

- [WhatsApp templates](/docs/guides/whatsapp/templates): the catalog, categories, and the shared send-by-template contract
- [Template guidelines](/docs/knowledge-base/whatsapp/template-guidelines): what Meta's review looks for
- [Phone number setup](/docs/guides/whatsapp/phone-number-setup): connecting the account you author on
- [How sending works](/docs/guides/whatsapp/sending-whatsapp): the full send payload