> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brainstormer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# WhatsApp Business Templates

> Create, list and delete Meta message templates, and choose the one that carries an operator's reply outside the 24-hour window.

# WhatsApp Business Templates

Outside WhatsApp's 24-hour customer-service window Meta only delivers **approved message templates**, so these endpoints are what allow business-initiated messaging at all.

Templates live at Meta on the connected **WhatsApp Business Account (WABA)**, and Brainstormer keeps a local mirror so a send decision never needs a Graph API round trip. Every endpoint below resolves the agent's connected channel to its WABA first; an agent with no active WhatsApp Business connection gets a `404`.

<Note>
  All API requests require a valid JWT token in the `Authorization: Bearer <token>` header. The API Gateway decodes the JWT and forwards auth context (`user-id`, `organization-id`, `user-email`, `x-platform-role`, `x-org-role`) as headers to downstream services.
</Note>

<Note>
  Meta reviews templates **asynchronously**. A create returns immediately with
  `status: "PENDING"` — never `APPROVED` — and the verdict arrives later on
  Meta's `message_template_status_update` webhook, which Brainstormer records
  automatically. **Only a template whose status is `APPROVED` can be sent.**
</Note>

## List Templates

<ParamField method="GET" path="/api/bot/bots/{botId}/whatsapp-business/templates" />

Reconciles the local mirror from Meta and returns the result. This makes a Graph API call, so it is slower than a local read — don't poll it.

### Response (200)

<ResponseField name="templates" type="object[]">
  <Expandable title="Template object">
    <ResponseField name="id" type="string">Brainstormer's id for the local row.</ResponseField>
    <ResponseField name="metaTemplateId" type="string | null">Meta's own template id, when known.</ResponseField>
    <ResponseField name="name" type="string">Meta's template name (lowercase letters, digits and underscores).</ResponseField>
    <ResponseField name="language" type="string">Meta language code, e.g. `en_US`.</ResponseField>
    <ResponseField name="category" type="string">`MARKETING`, `UTILITY` or `AUTHENTICATION`. Meta may change this during review.</ResponseField>
    <ResponseField name="status" type="string">Meta's review status, verbatim: `APPROVED`, `PENDING`, `REJECTED`, `PAUSED`, `DISABLED`, `FLAGGED`, `IN_APPEAL`, `ARCHIVED`, `LOCKED`, `LIMIT_EXCEEDED`, `PENDING_DELETION`, `DELETED` or `UNKNOWN`.</ResponseField>
    <ResponseField name="statusReason" type="string | null">Meta's reason for a rejection or pause.</ResponseField>
    <ResponseField name="statusUpdatedAt" type="string | null">ISO timestamp of the last status change.</ResponseField>
    <ResponseField name="parameterFormat" type="string">`POSITIONAL` (`{{1}}`) or `NAMED` (`{{customer_name}}`).</ResponseField>
    <ResponseField name="components" type="object[]">Meta's component definitions, verbatim.</ResponseField>

    <ResponseField name="fill" type="object">
      What must be supplied to send this template. Derived from the definition, so it can never go stale.

      <Expandable title="fill">
        <ResponseField name="headerTokens" type="string[]">Placeholder keys in the header text.</ResponseField>
        <ResponseField name="bodyTokens" type="string[]">Placeholder keys in the body, in Meta's fill order.</ResponseField>
        <ResponseField name="headerMedia" type="string | null">`IMAGE`, `VIDEO` or `DOCUMENT` when the header needs a media link.</ResponseField>
        <ResponseField name="isStatic" type="boolean">True when nothing has to be supplied.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="usableAsFallback" type="boolean">Whether this template can carry an operator's reply — exactly one body placeholder and nothing else to fill.</ResponseField>
    <ResponseField name="createdAt" type="string">ISO timestamp.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="fallbackTemplate" type="object | null">
  The template currently configured to carry operator replies outside the window, as `{ name, language }`.
</ResponseField>

<ResponseField name="recommendedFallback" type="object">
  A ready-made draft the UI offers as a starting point.
</ResponseField>

### Errors

| Status | Code | Meaning |
| - | - | - |
| `404` | `whatsapp_business_not_connected` | The agent has no active WhatsApp Business connection. |
| `409` | `whatsapp_business_waba_unknown` | The connection predates WABA-id recording; reconnect the number. |
| `502` | `whatsapp_business_token_invalid` | Meta rejected the channel's credentials — reconnect the channel. |

***

## Create a Template

<ParamField method="POST" path="/api/bot/bots/{botId}/whatsapp-business/templates" />

Files a new template with Meta. Answers `201` with the stored template in its `PENDING` state.

### Body

<ParamField body="name" type="string" required>
  Lowercase letters, digits and underscores only, and unique on the WABA for this language.
</ParamField>

<ParamField body="language" type="string" required>
  Meta language code, e.g. `en_US`.
</ParamField>

<ParamField body="category" type="string" required>
  `MARKETING`, `UTILITY` or `AUTHENTICATION`.
</ParamField>

<ParamField body="body" type="string" required>
  The message text. Use `{{1}}`, `{{2}}` … for values supplied at send time. Meta rejects a body that is only a placeholder.
</ParamField>

<ParamField body="headerText" type="string">
  Optional header text, which may also carry placeholders.
</ParamField>

<ParamField body="footer" type="string">
  Optional footer text. Cannot contain placeholders.
</ParamField>

<ParamField body="examples" type="object">
  Sample values per placeholder, as `{ header?: { "1": "…" }, body?: { "1": "…" } }`. **Meta requires an example for every placeholder** before a reviewer will look at the template.
</ParamField>

```json Example theme={null}
{
  "name": "support_follow_up",
  "language": "en_US",
  "category": "UTILITY",
  "body": "You have a new reply from our support team:\n\n{{1}}\n\nReply to this message to continue the conversation.",
  "examples": { "body": { "1": "Thanks for waiting — your order is on its way." } }
}
```

### Errors

| Status | Code | Meaning |
| - | - | - |
| `400` | `whatsapp_business_template_name_invalid` | The name is outside Meta's alphabet. |
| `400` | `whatsapp_business_template_parameters_invalid` | A placeholder has no sample value, or the draft is otherwise malformed. Refused before any Graph call. |
| `400` | — | Meta refused the create (duplicate name, unsupported component). |
| `502` | `whatsapp_business_token_invalid` | Meta rejected the channel's credentials. |

***

## Delete a Template

<ParamField method="DELETE" path="/api/bot/bots/{botId}/whatsapp-business/templates/{templateId}" />

Deletes at Meta first, then locally. If this template was the configured fallback, that selection is cleared in the same request.

Returns `204`. A `404` with code `whatsapp_business_template_not_found` means the template is not on this account.

***

## Set the Fallback Template

<ParamField method="PUT" path="/api/bot/bots/{botId}/whatsapp-business/templates/fallback" />

Chooses the approved template that carries an **operator's reply** when the 24-hour customer-service window has closed. The operator's own text is placed in the template's single body placeholder.

### Body

<ParamField body="name" type="string">
  Template name. Omit (or send an empty body) to clear the selection.
</ParamField>

<ParamField body="language" type="string">
  Template language. Required when `name` is given.
</ParamField>

### Response (200)

<ResponseField name="fallbackTemplate" type="object | null">
  `{ name, language }`, or `null` when cleared.
</ResponseField>

### Errors

| Status | Code | Meaning |
| - | - | - |
| `404` | `whatsapp_business_template_not_found` | No such template on this WABA. |
| `409` | `whatsapp_business_template_not_approved` | Meta has not approved it yet. |
| `409` | `whatsapp_business_template_shape_unsuitable` | It does not have exactly one body placeholder and nothing else to fill — there is nowhere to put the operator's message. |

<Warning>
  A template send is billed twice over: Brainstormer's own
  `WHATSAPP_BUSINESS_CREDITS_PER_TEMPLATE_SEND` platform fee, and Meta's real
  per-message charge (recorded from the delivery-status webhook). A reply sent
  **inside** the 24-hour window incurs neither of those template charges.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.