<Intro>

<EndpointHeader />

<Description>

Sends a WhatsApp message built from a message template to one recipient.
Name the template, optionally pick its language variant, and fill its
placeholders in `components`; Bird selects the sender number from the
template's category, so the request carries no sender field. Templates are
the only supported content type: a request without `template` is rejected
with a `422`. Browse what you can send with
[List available message templates](/docs/api/reference/list-whats-app-templates).

The `202` response is the accepted message, echoing the resolved template
and language; it is not a delivery confirmation. Follow delivery with
[Get a WhatsApp message](/docs/api/reference/get-whats-app-message), the
per-message timeline from
[List events for a WhatsApp message](/docs/api/reference/list-whats-app-message-events),
or `whatsapp.*` webhook events.

A template name or language the catalogue does not stock, parameter values
that do not match the template's declared placeholders, and a recipient
that is not a valid phone number each return a `422`.

</Description>

</Intro>

<Payload kind="request">

<Field name="to" type="string" required>

<Description>

The message recipient's phone number in E.164 format (for example `+31612345678`). A value that is not a valid phone number returns a `422` `WhatsAppInvalidRecipient`.

</Description>

</Field>

<Field name="template" type="object">

<Description>

The template to send. Bird selects the sender number from the template's category, so there is no sender field on this request. Templates are the only supported content type today: a request without one is rejected with a `422`.

</Description>

<FieldChildren kind="request">

<Field name="name" type="object" prefix="template." required>

<Description>

The template to send, by its name (for example `bird_otp`).

</Description>

</Field>

<Field name="language" type="object" prefix="template.">

<Description>

Language code of the template variant to send (for example `en` or `pt_BR`). May be omitted when the template has a single language; when it is stocked in several, omitting the language returns a `422` that names the available codes. The accepted message echoes the resolved language.

</Description>

</Field>

<Field name="components" type="array of object" prefix="template.">

<Description>

The values that fill the template's placeholders: one entry per content block that has placeholders, each carrying its `parameters` in `{{n}}` order. Parameter counts must match the template's declared placeholders exactly, or the send returns a `422` `WhatsAppTemplateParameterMismatch`.

</Description>

<FieldChildren kind="request">

<Field name="type" type="string" prefix="template.components." required>

<Description>

Which part of the template this fills in: `body` for the main text, `button` for a button's variable, `header` for the header. Bird manages header values itself, so a `header` entry supplied on a send is ignored.

</Description>

<Description>

Possible values (may grow over time): `header`, `body`, `button`

</Description>

</Field>

<Field name="parameters" type="array of object" prefix="template.components.">

<Description>

The values that fill this part's placeholders, in `{{n}}` placeholder order.

</Description>

<FieldChildren kind="request">

<Field name="type" type="object" prefix="template.components.parameters." required>

<Description>

The kind of value this parameter carries. `text` is the only kind today.

</Description>

</Field>

<Field name="text" type="string" prefix="template.components.parameters." required>

<Description>

The value substituted into the placeholder, as a plain string.

</Description>

</Field>

<Field name="name" type="string" prefix="template.components.parameters.">

<Description>

For named-parameter templates: the placeholder this value fills (for example `first_name`). Omit for positional templates.

</Description>

</Field>

</FieldChildren>

</Field>

</FieldChildren>

</Field>

</FieldChildren>

</Field>

<Field name="tags" type="array of object">

<Description>

Structured `{name, value}` labels for filtering. Tags become first-class query dimensions: filter the list endpoint by tag name. Maximum 20 tags per send. Use tags for low-cardinality dimensions (`category`, `experiment_variant`). For arbitrary structured context you do not need as a filter dimension, use `metadata` instead.

</Description>

<FieldChildren kind="request">

<Field name="name" type="string" prefix="tags." required>

<Description>

Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.

</Description>

</Field>

<Field name="value" type="string" prefix="tags." required>

<Description>

Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="metadata" type="object">

<Description>

Arbitrary JSON object stored on the message and returned on API reads. Maximum 2 KB serialized. Use metadata for per-send context like internal IDs and foreign keys. For low-cardinality filterable labels, use `tags` instead.

</Description>

</Field>

</Payload>

<Payload kind="response">

<Field name="id" type="string" required>

<Description>

ID of the message (`wam_`-prefixed), assigned when the send is accepted. Pass it as `message_id` to the get-message and list-events endpoints.

</Description>

</Field>

<Field name="direction" type="string" required>

<Description>

Whether the message was sent by the business (`outbound`) or received from the contact (`inbound`).

</Description>

<Description>

Possible values: `outbound`, `inbound`

</Description>

</Field>

<Field name="from" type="object" required>

<Description>

Sender of the message. On outbound messages, the business number it was sent from; on inbound, the WhatsApp contact.

</Description>

<FieldChildren kind="response">

<Field name="phone_number" type="string" prefix="from.">

<Description>

Phone number in E.164 format, when known.

</Description>

</Field>

<Field name="bsuid" type="string" prefix="from.">

<Description>

Business-scoped user ID, Meta's identifier for the WhatsApp user. Present only on the WhatsApp-user side of the message.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="to" type="object" required>

<Description>

Recipient of the message. On outbound messages, the WhatsApp contact; on inbound, the business number.

</Description>

<FieldChildren kind="response">

<Field name="phone_number" type="string" prefix="to.">

<Description>

Phone number in E.164 format, when known.

</Description>

</Field>

<Field name="bsuid" type="string" prefix="to.">

<Description>

Business-scoped user ID, Meta's identifier for the WhatsApp user. Present only on the WhatsApp-user side of the message.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="template" type="object">

<Description>

The template the message was sent from. For authentication templates the filled-in values are not returned.

</Description>

<FieldChildren kind="response">

<Field name="name" type="object" prefix="template." required>

<Description>

The template's stable handle (for example `bird_otp`).

</Description>

</Field>

<Field name="category" type="object" prefix="template." required>

<Description>

Content classification applied to messages sent from this template.

</Description>

</Field>

<Field name="language" type="string" prefix="template." required>

<Description>

The language code of the template variant that was sent (for example `en`).

</Description>

</Field>

<Field name="components" type="array of object" prefix="template." required>

<Description>

The values that filled the template's placeholders. Empty for an authentication template, whose content is never returned.

</Description>

<FieldChildren kind="response">

<Field name="type" type="string" prefix="template.components." required>

<Description>

Which part of the template this fills in: `body` for the main text, `button` for a button's variable, `header` for the header. Bird manages header values itself, so a `header` entry supplied on a send is ignored.

</Description>

<Description>

Possible values (may grow over time): `header`, `body`, `button`

</Description>

</Field>

<Field name="parameters" type="array of object" prefix="template.components.">

<Description>

The values that fill this part's placeholders, in `{{n}}` placeholder order.

</Description>

<FieldChildren kind="response">

<Field name="type" type="object" prefix="template.components.parameters." required>

<Description>

The kind of value this parameter carries. `text` is the only kind today.

</Description>

</Field>

<Field name="text" type="string" prefix="template.components.parameters." required>

<Description>

The value substituted into the placeholder, as a plain string.

</Description>

</Field>

<Field name="name" type="string" prefix="template.components.parameters.">

<Description>

For named-parameter templates: the placeholder this value fills (for example `first_name`). Omit for positional templates.

</Description>

</Field>

</FieldChildren>

</Field>

</FieldChildren>

</Field>

</FieldChildren>

</Field>

<Field name="status" type="object" required />

<Field name="last_error" type="nullable object">

<Description>

Failure detail for a message that did not reach the recipient. Present only when the message failed.

</Description>

<FieldChildren kind="response">

<Field name="code" type="string" prefix="last_error." required>

<Description>

Failure reason, uniform whether the failure happened internally or was reported by the WhatsApp network. `insufficient_balance`: the workspace could not afford the send. `price_not_found`: no price was configured for this destination/template combination. `internal_error`: an unexpected Bird-side failure. `undeliverable`: the recipient could not be reached (for example not on WhatsApp, or the number is invalid). `service_window_expired`: the 24-hour customer care window has closed and a free-form message cannot be sent; send a template instead. `rate_limited`: the send was throttled. `recipient_suppressed`: the recipient is on the workspace's suppression list; the message was rejected before sending. Open enum: new codes may be added over time, so treat any unrecognized value as a future code rather than an error.

</Description>

<Description>

Possible values (may grow over time): `insufficient_balance`, `price_not_found`, `internal_error`, `undeliverable`, `service_window_expired`, `rate_limited`, `recipient_suppressed`

</Description>

</Field>

<Field name="description" type="string" prefix="last_error." required>

<Description>

Human-readable explanation of the failure.

</Description>

</Field>

<Field name="meta_error_code" type="nullable string" prefix="last_error.">

<Description>

Raw error code from the WhatsApp Cloud API, when available, for low-level debugging.

</Description>

</Field>

<Field name="occurred_at" type="string" prefix="last_error." required>

<Description>

When the failure occurred.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="created_at" type="string" required>

<Description>

When the message was accepted for delivery.

</Description>

</Field>

<Field name="sent_at" type="nullable string">

<Description>

When the message was handed to the WhatsApp network. Null until then.

</Description>

</Field>

<Field name="delivered_at" type="nullable string">

<Description>

When delivery was confirmed. Null until then.

</Description>

</Field>

<Field name="read_at" type="nullable string">

<Description>

When the message was read by the recipient. Null until then.

</Description>

</Field>

<Field name="tags" type="array of object">

<Description>

Structured `{name, value}` filter labels applied to this message.

</Description>

<FieldChildren kind="response">

<Field name="name" type="string" prefix="tags." required>

<Description>

Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.

</Description>

</Field>

<Field name="value" type="string" prefix="tags." required>

<Description>

Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="metadata" type="object">

<Description>

Arbitrary JSON metadata stored on the message.

</Description>

</Field>

</Payload>