---
title: "Send template"
description: "Send approved template notifications (order updates, reminders)."
---

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

# Send template

Use templates when you need to **reach out first** — order updates, OTP codes, appointment reminders — especially outside the 24-hour reply window. Each template is pre-approved by Meta.

:::note
Concepts: [Templates overview](/templates/overview) | [Authentication & OTP templates](/templates/authentication-otp)
:::

A template send needs more than name and language. Pass **components** that match how the template was built: body variables, optional header image or text, and button URL parameters when the template defines them.

## Before you send

1. **Create and approve the template**

   Create the template in the [Wazapin app](https://app.wazapin.com) or sync from Meta on official channels. Status must be **approved**.

   `GET /v1/templates` lists templates for your workspace. See [Channel support](/api/channel-support) for official vs unofficial behavior.

2. **Read the template structure**

   Open the template in the app and note:

- **Header** — none, text (`{{1}}`), image, video, or document
- **Body** — static text with `{{1}}`, `{{2}}`, … placeholders
- **Buttons** — quick reply, URL (may include `{{1}}` in the link), copy code, etc.

   Your `components` array must mirror these sections in order.

3. **Build the components array**

   Each section becomes one object in `content.template.components`:

   | Component `type` | When required |
   | --- | --- |
   | `header` | Template has a dynamic header (text, image, video, document) |
   | `body` | Template body has `{{n}}` variables |
   | `button` | URL or other dynamic button parameters (`sub_type`, `index`) |

   Omit a section if the template has no variables in that part.

## Request shape

All template sends use `POST /v1/messages` with `type: "template"` and a nested `content.template` object (Meta-compatible shape).

| Field | Required | Description |
| --- | --- | --- |
| `channel_id` | Yes | Connected WhatsApp channel |
| `to` | Yes | Recipient phone (international format) |
| `type` | Yes | `template` |
| `content.template.name` | Yes | Approved template name |
| `content.template.language.code` | Yes | Locale, e.g. `en_US`, `id` |
| `content.template.components` | Often | Parameters per header/body/button |

:::warning
Do **not** use a flat shape like `content.template_name` at the top level — the API requires `content.template.name` and `content.template.language.code`.
:::

## Example: body variables only

Template body: `Hi {{1}}, your order {{2}} is on the way.`

### cURL

```bash
curl -X POST "https://api.wazapin.com/v1/messages" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
"channel_id": "wzp_abc123",
"to": "6281234567890",
"type": "template",
"content": {
  "template": {
    "name": "order_shipped",
    "language": { "code": "en_US" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "John" },
          { "type": "text", "text": "ORD-42" }
        ]
      }
    ]
  }
}
  }'
```
### TypeScript

```typescript
import { WazapinClient, templateMessage } from "@wazapin/sdk";

const wazapin = new WazapinClient({ apiKey: process.env.WAZAPIN_API_KEY! });

const { data: message } = await wazapin.messages.send(
  templateMessage({
channel_id: "wzp_abc123",
to: "6281234567890",
name: "order_shipped",
language: "en_US",
components: [
  {
    type: "body",
    parameters: [
      { type: "text", text: "John" },
      { type: "text", text: "ORD-42" },
    ],
  },
],
  }),
);

console.log(message.id);
```
### Python

```python
import requests

response = requests.post(
"https://api.wazapin.com/v1/messages",
headers={"X-Api-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
json={
    "channel_id": "wzp_abc123",
    "to": "6281234567890",
    "type": "template",
    "content": {
        "template": {
            "name": "order_shipped",
            "language": {"code": "en_US"},
            "components": [
                {
                    "type": "body",
                    "parameters": [
                        {"type": "text", "text": "John"},
                        {"type": "text", "text": "ORD-42"},
                    ],
                }
            ],
        }
    },
},
timeout=30,
)
response.raise_for_status()
print(response.json())
```

## Example: header image + body

Template with an **image header** and one body variable.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "promo_with_image",
  "language": { "code": "id" },
  "components": [
    {
      "type": "header",
      "parameters": [
        {
          "type": "image",
          "image": { "link": "https://cdn.example.com/promo.jpg" }
        }
      ]
    },
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Ramadan Sale" }
      ]
    }
  ]
}
  }
}
```

## Example: header text variable

Template header text: `Hello {{1}}` — the header component lives inside `content.template.components`.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "greeting_header",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "header",
      "parameters": [
        { "type": "text", "text": "John" }
      ]
    },
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Your appointment is confirmed." }
      ]
    }
  ]
}
  }
}
```

## Example: dynamic URL button

Template with a URL button containing `{{1}}` in the path — button component nested in the same `components` array.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "order_tracking",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "John" },
        { "type": "text", "text": "ORD-42" }
      ]
    },
    {
      "type": "button",
      "sub_type": "url",
      "index": "0",
      "parameters": [
        { "type": "text", "text": "ORD-42" }
      ]
    }
  ]
}
  }
}
```

`index` is the zero-based button index in the template definition. `sub_type` matches the button type Meta approved (`url`, `quick_reply`, `copy_code`, etc.).

List and sync templates with `wazapin.templates.list()` and `wazapin.templates.sync()`. See [SDK templates](/sdk/templates) (advanced).

## Common mistakes

| Mistake | Fix |
| --- | --- |
| Template not approved | Wait for Meta approval or pick another name |
| Wrong `language.code` | Must match the template locale exactly (`en_US` vs `en`) |
| Missing `components` | Add parameters for every `{{n}}` in header/body/buttons |
| Flat `template_name` in `content` | Use nested `content.template.name` |
| Header image URL not HTTPS | Use a public HTTPS URL Meta can fetch |

## Related

- [Sending overview](/getting-started/sending-overview) — session vs template
- [Example: send template](/recipes/send-template-message)
- [Send a message](/api-reference/messages/send-a-message) — API reference and playground
- [Channel support](/api/channel-support) — unofficial channel fallback behavior

Source: https://docs.wazapin.id/getting-started/send-template/index.mdx
