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

# Templates

> Understanding message templates for multi-channel messaging

# Templates

Templates are reusable message formats for sending structured messages across WhatsApp, SMS, Telegram, Instagram, and Email. They are especially important for WhatsApp, which requires template approval for business-initiated conversations.

## Why Templates?

Templates solve three key problems:

1. **WhatsApp Compliance**: Meta requires businesses to use approved templates when initiating conversations outside the 24-hour window
2. **Consistency**: Ensure your transactional messages follow a consistent format
3. **Multi-Channel Delivery**: Define one template with channel-specific bodies, then send it on any channel

## Template Structure

A template consists of:

```json theme={null}
{
  "id": "tmpl_abc123",
  "name": "order_confirmation",
  "language": "en",
  "body": "Hi {{1}}, your order {{2}} has been confirmed!",
  "category": "UTILITY",
  "variables": ["customer_name", "order_id"],
  "status": "approved"
}
```

### Fields

| Field       | Description                                                                       |
| ----------- | --------------------------------------------------------------------------------- |
| `name`      | Unique identifier (lowercase, underscores)                                        |
| `language`  | Language code (e.g., "en", "es", "pt")                                            |
| `body`      | Message text with variable placeholders                                           |
| `category`  | WhatsApp category (required for WhatsApp submission, optional for other channels) |
| `variables` | List of variable names for documentation                                          |
| `status`    | Approval status                                                                   |

## Template Variables

Templates support dynamic content through variables. Two formats are supported:

### Numbered Variables (WhatsApp Native)

Use `{{1}}`, `{{2}}`, `{{3}}` for WhatsApp compatibility:

```
"Hi {{1}}, your order {{2}} ships on {{3}}"
```

### Named Variables

Use `{{name}}` for better readability:

```
"Hi {{customer_name}}, your order {{order_id}} ships on {{date}}"
```

<Note>
  Named variables are automatically converted to numbered format when sending via WhatsApp.
</Note>

### Contact Variables

Use `{{contact.*}}` variables to auto-resolve values from the recipient's contact metadata:

| Variable                   | Resolved From                 |
| -------------------------- | ----------------------------- |
| `{{contact.first_name}}`   | Contact metadata `first_name` |
| `{{contact.last_name}}`    | Contact metadata `last_name`  |
| `{{contact.phone}}`        | Contact's phone number        |
| `{{contact.email}}`        | Contact's primary email       |
| `{{contact.country}}`      | Contact's country code        |
| `{{contact.custom_field}}` | Any custom metadata field     |

```
"Hola {{contact.first_name}}, tu pedido esta listo."
```

When the message is sent, Zavu looks up the recipient's contact record and replaces these variables automatically. If the contact doesn't exist or the field is empty, the variable is replaced with an empty string.

<Tip>
  Contact variables work on all channels: SMS, Telegram, Instagram, and Email. You can also pass explicit values via `templateVariables` as a fallback.
</Tip>

## WhatsApp Categories

WhatsApp requires every template to be categorized:

| Category           | Use Case               | Examples                                                     |
| ------------------ | ---------------------- | ------------------------------------------------------------ |
| **UTILITY**        | Transactional messages | Order confirmations, shipping updates, appointment reminders |
| **MARKETING**      | Promotional content    | Sales, offers, newsletters                                   |
| **AUTHENTICATION** | Verification codes     | OTPs, login codes, 2FA                                       |

<Warning>
  Marketing templates have stricter approval requirements and may have different pricing. Use UTILITY for transactional messages.
</Warning>

## Authentication Templates

Authentication templates are used for sending verification codes (OTPs) to users. These templates have special requirements and behavior.

### Requirements

<Warning>
  Meta requires your WhatsApp Business Account to meet these criteria before you can create AUTHENTICATION templates:

  1. **Business Verification**: Your business must be verified by Meta
  2. **Conversation History**: At least **2,000 business-initiated conversations** in the last 30 days

  If your account doesn't meet these requirements, AUTHENTICATION templates will fail with a permission error.
</Warning>

### How Authentication Templates Work

Unlike regular templates, authentication templates have a **pre-defined message format** controlled by Meta. When you create an authentication template:

* The message body is **automatically generated** by Meta
* The format is: `{{1}} is your verification code.`
* You cannot customize the body text

### OTP Button Types

Authentication templates support two types of OTP buttons:

| Button Type    | Description                | Use Case                                 |
| -------------- | -------------------------- | ---------------------------------------- |
| **COPY\_CODE** | Shows a "Copy Code" button | User manually copies and pastes the code |
| **ONE\_TAP**   | Enables Android autofill   | Automatic code entry on Android devices  |

### ONE\_TAP Button Requirements

For ONE\_TAP buttons (Android autofill), you must provide:

```json theme={null}
{
  "buttons": [{
    "type": "otp",
    "text": "Autofill",
    "otpType": "ONE_TAP",
    "packageName": "com.yourapp.package",
    "signatureHash": "your_app_signature_hash"
  }]
}
```

| Field           | Description                                            |
| --------------- | ------------------------------------------------------ |
| `packageName`   | Your Android app's package name                        |
| `signatureHash` | Your Android app's signature hash for SMS verification |

### Optional Security Features

Authentication templates can include:

| Feature                     | Description                                          |
| --------------------------- | ---------------------------------------------------- |
| `addSecurityRecommendation` | Adds "Do not share this code with anyone" disclaimer |
| `codeExpirationMinutes`     | Shows expiration time (1-90 minutes) in footer       |

### Creating an Authentication Template

```bash theme={null}
curl -X POST https://api.zavu.dev/v1/templates \
  -H "Authorization: Bearer zv_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "verification_code",
    "language": "en",
    "body": "",
    "whatsappCategory": "AUTHENTICATION",
    "buttons": [{
      "type": "otp",
      "text": "Copy Code",
      "otpType": "COPY_CODE"
    }],
    "addSecurityRecommendation": true,
    "codeExpirationMinutes": 10
  }'
```

<Note>
  The `body` field should be empty or omitted for authentication templates. Meta will automatically generate the message body.
</Note>

## WhatsApp Approval Workflow

WhatsApp templates must be approved by Meta before use:

```
Create Template (draft)
        |
        v
Submit to WhatsApp (pending)
        |
        v
Meta Reviews (24-48 hours)
        |
    +---+---+
    |       |
Approved  Rejected
    |       |
    v       v
Ready    Fix & Resubmit
```

### Template Statuses

| Status     | Description                     |
| ---------- | ------------------------------- |
| `draft`    | Created but not submitted       |
| `pending`  | Submitted, awaiting Meta review |
| `approved` | Ready to use                    |
| `rejected` | Rejected by Meta (see reason)   |

## Creating Templates

### Via API

```bash theme={null}
curl -X POST https://api.zavu.dev/v1/templates \
  -H "Authorization: Bearer zv_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order_shipped",
    "language": "en",
    "body": "Hi {{1}}, your order {{2}} has shipped and will arrive by {{3}}.",
    "whatsappCategory": "UTILITY",
    "variables": ["customer_name", "order_id", "delivery_date"]
  }'
```

### Response

```json theme={null}
{
  "id": "tmpl_xyz789",
  "name": "order_shipped",
  "language": "en",
  "body": "Hi {{1}}, your order {{2}} has shipped...",
  "category": "UTILITY",
  "status": "draft",
  "variables": ["customer_name", "order_id", "delivery_date"],
  "createdAt": "2025-01-15T10:30:00Z"
}
```

## Rich Template Components

WhatsApp templates can include rich components:

### Header

Add a header with text, image, video, or document:

```json theme={null}
{
  "headerType": "image",
  "headerContent": "https://example.com/logo.png"
}
```

### Footer

Add a footer text:

```json theme={null}
{
  "footer": "Reply STOP to unsubscribe"
}
```

### Buttons

Add interactive buttons:

```json theme={null}
{
  "buttons": [
    {
      "type": "quick_reply",
      "text": "Track Order"
    },
    {
      "type": "url",
      "text": "View Details",
      "url": "https://example.com/orders/{{1}}"
    }
  ]
}
```

## Sending Template Messages

To send a template message, specify the template ID and variables:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await client.messages.send({
    to: "+14155551234",
    messageType: "template",
    content: {
      templateId: "tmpl_abc123",
      templateVariables: {
        "1": "John",
        "2": "ORD-12345",
        "3": "January 20th"
      }
    }
  });
  ```

  ```python Python theme={null}
  client.messages.send(
      to="+14155551234",
      message_type="template",
      content={
          "templateId": "tmpl_abc123",
          "templateVariables": {
              "1": "John",
              "2": "ORD-12345",
              "3": "January 20th"
          }
      }
  )
  ```

  ```ruby Ruby theme={null}
  client.messages.send(
    to: "+14155551234",
    message_type: "template",
    content: {
      templateId: "tmpl_abc123",
      templateVariables: {
        "1": "John",
        "2": "ORD-12345",
        "3": "January 20th"
      }
    }
  )
  ```

  ```go Go theme={null}
  result, err := client.Messages.Send(context.TODO(), zavudev.MessageSendParams{
      To:          zavudev.String("+14155551234"),
      MessageType: zavudev.String("template"),
      Content: &zavudev.MessageContentParams{
          TemplateID: zavudev.String("tmpl_abc123"),
          TemplateVariables: map[string]string{
              "1": "John",
              "2": "ORD-12345",
              "3": "January 20th",
          },
      },
  })
  ```

  ```php PHP theme={null}
  $result = $client->messages->send([
      'to' => '+14155551234',
      'messageType' => 'template',
      'content' => [
          'templateId' => 'tmpl_abc123',
          'templateVariables' => [
              '1' => 'John',
              '2' => 'ORD-12345',
              '3' => 'January 20th',
          ],
      ],
  ]);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.zavu.dev/v1/messages \
    -H "Authorization: Bearer zv_live_xxx" \
    -H "Content-Type: application/json" \
    -d '{
      "to": "+14155551234",
      "messageType": "template",
      "content": {
        "templateId": "tmpl_abc123",
        "templateVariables": {
          "1": "John",
          "2": "ORD-12345",
          "3": "January 20th"
        }
      }
    }'
  ```
</CodeGroup>

### Templates with Dynamic URL Buttons

If your template has a URL button with a `{{1}}` placeholder (e.g., `https://example.com/orders/{{1}}`), pass the value through `templateButtonVariables`. Keys are the **button index** (0-based) in the template's `buttons` array.

```bash theme={null}
curl -X POST https://api.zavu.dev/v1/messages \
  -H "Authorization: Bearer zv_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155551234",
    "messageType": "template",
    "content": {
      "templateId": "tmpl_abc123",
      "templateVariables": {
        "1": "John"
      },
      "templateButtonVariables": {
        "0": "ORD-12345"
      }
    }
  }'
```

| Field                     | Keys                                 | What it substitutes                              |
| ------------------------- | ------------------------------------ | ------------------------------------------------ |
| `templateVariables`       | Position in body (`"1"`, `"2"`, ...) | Body placeholders                                |
| `templateButtonVariables` | Button index (`"0"`, `"1"`, `"2"`)   | The `{{1}}` placeholder inside that button's URL |

<Warning>
  WhatsApp URL buttons only accept `{{1}}` (positional, single digit, no whitespace, no name). Named or padded placeholders like `{{token}}` or `{{ 1 }}` are stored as literal text and cannot be substituted. See the [WhatsApp templates guide](/guides/whatsapp/templates/sending#sending-templates-with-dynamic-url-buttons) for the full ruleset.
</Warning>

## Multi-Channel Templates

Templates support channel-specific bodies. When a message is sent, Zavu uses the channel-specific body if available, falling back to the default `body`.

```json theme={null}
{
  "name": "order_confirmation",
  "body": "Hi {{1}}, your order {{2}} is confirmed!",
  "smsBody": "Order {{2}} confirmed for {{1}}. Track at example.com",
  "telegramBody": "Hi {{1}}, your order {{2}} is confirmed! Check your Telegram for updates.",
  "instagramBody": "Hi {{1}}! Order {{2}} confirmed.",
  "emailSubject": "Order {{2}} Confirmed",
  "emailHtmlBody": "<h1>Order Confirmed</h1><p>Hi {{1}}...</p>"
}
```

| Field           | Channel                  | Fallback            |
| --------------- | ------------------------ | ------------------- |
| `body`          | Default for all channels | -                   |
| `smsBody`       | SMS, SMS One-Way         | `body`              |
| `telegramBody`  | Telegram                 | `body`              |
| `instagramBody` | Instagram                | `body`              |
| `emailSubject`  | Email subject            | -                   |
| `emailHtmlBody` | Email HTML body          | `body` (plain text) |

### Sending Templates on Different Channels

<CodeGroup>
  ```bash Telegram theme={null}
  curl -X POST https://api.zavu.dev/v1/messages \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "to": "123456789",
      "channel": "telegram",
      "messageType": "template",
      "content": {
        "templateId": "tmpl_abc123",
        "templateVariables": { "1": "John", "2": "ORD-12345" }
      }
    }'
  ```

  ```bash SMS theme={null}
  curl -X POST https://api.zavu.dev/v1/messages \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "to": "+14155551234",
      "channel": "sms",
      "messageType": "template",
      "content": {
        "templateId": "tmpl_abc123",
        "templateVariables": { "1": "John", "2": "ORD-12345" }
      }
    }'
  ```
</CodeGroup>

<Note>
  WhatsApp templates require Meta approval before use. Templates on SMS, Telegram, and Instagram do not require external approval.
</Note>

## Managing Templates

### List Templates

```bash theme={null}
curl https://api.zavu.dev/v1/templates \
  -H "Authorization: Bearer zv_live_xxx"
```

### Get Template

```bash theme={null}
curl https://api.zavu.dev/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer zv_live_xxx"
```

### Delete Template

```bash theme={null}
curl -X DELETE https://api.zavu.dev/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer zv_live_xxx"
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Keep It Short" icon="compress">
    WhatsApp has character limits. Keep your templates concise and actionable.
  </Card>

  <Card title="Use UTILITY Category" icon="wrench">
    For transactional messages, always use UTILITY to improve approval chances.
  </Card>

  <Card title="Test Before Launch" icon="flask">
    Create templates early in development. Approval can take 24-48 hours.
  </Card>

  <Card title="Handle Rejections" icon="rotate">
    If rejected, review Meta's guidelines, fix the issue, and resubmit.
  </Card>
</CardGroup>

## Common Rejection Reasons

| Reason                         | Solution                                                    |
| ------------------------------ | ----------------------------------------------------------- |
| Promotional content in UTILITY | Change category to MARKETING or remove promotional language |
| Missing variable examples      | Provide clear example values for each variable              |
| Inappropriate content          | Review WhatsApp commerce policy                             |
| Poor grammar/spelling          | Proofread and fix language errors                           |

## Next Steps

<CardGroup cols={2}>
  <Card title="WhatsApp Templates" icon="whatsapp" href="/guides/whatsapp/templates/sending">
    Deep dive into WhatsApp template features
  </Card>

  <Card title="Senders" icon="paper-plane" href="/concepts/senders">
    Learn how templates work with senders
  </Card>
</CardGroup>
