# Create template

> Create a WhatsApp message template. Note: Templates must be approved by Meta before use.

`POST https://api.zavu.dev/v1/templates`

Create a WhatsApp message template. Note: Templates must be approved by Meta before use.

## Body

- `name` (string, required)
- `language` (string, required)
- `body` (string, required): Default template body. Used when no channel-specific body is set.
- `smsBody` (string): Channel-specific body for SMS. Falls back to `body` if not set.
- `telegramBody` (string): Channel-specific body for Telegram. Falls back to `body` if not set.
- `instagramBody` (string): Channel-specific body for Instagram. Falls back to `body` if not set.
- `whatsappCategory` (string): WhatsApp template category. One of: `UTILITY`, `MARKETING`, `AUTHENTICATION`.
- `headerType` (string): Type of header for the template. One of: `text`, `image`, `video`, `document`.
- `headerContent` (string): Header content (text string or media URL).
- `footer` (string): Footer text for the template.
- `variables` (string[])
- `buttons` (object[]): Template buttons (max 3).
  - `type` (string, required): `request_contact_info` renders a fixed **Share Contact Info** button that asks the recipient to share their phone number — useful when a contact adopted a WhatsApp username and you only know their BSUID. It takes no other fields. One of: `quick_reply`, `url`, `phone`, `otp`, `request_contact_info`.
  - `text` (string): Button label. Required for every type except `request_contact_info`, whose label is fixed by WhatsApp.
  - `url` (string<uri>): Button destination. Use `{{1}}` exactly once for a dynamic URL (e.g. `https://example.com/orders/{{1}}`); WhatsApp only accepts the strict `{{1}}` form. Static URLs must not contain any `{{...}}` placeholder.
  - `example` (string): Sample value Meta uses to review templates with a dynamic URL button. Substituted into `{{1}}` of the URL when the template is submitted to Meta. Only meaningful when `url` contains `{{1}}`; ignored for static URLs.
  - `phoneNumber` (string)
  - `otpType` (string): Required when type is 'otp'. COPY_CODE shows copy button, ONE_TAP enables Android autofill. One of: `COPY_CODE`, `ONE_TAP`.
  - `packageName` (string): Android package name. Required for ONE_TAP buttons.
  - `signatureHash` (string): Android app signature hash. Required for ONE_TAP buttons.
- `addSecurityRecommendation` (boolean): Add 'Do not share this code' disclaimer. Only for AUTHENTICATION templates.
- `codeExpirationMinutes` (integer): Code expiration time in minutes. Only for AUTHENTICATION templates.

## Responses

### 201

Template created.

- `id` (string, required)
- `name` (string, required): Template name. For WhatsApp, must match the approved template name in Meta.
- `language` (string, required): Language code.
- `body` (string, required): Default template body with variables: positional ({{1}}, {{2}}) or named ({{customer_name}}, {{contact.first_name}}). Templates created in Zavu are submitted to Meta as positional; templates imported from a WhatsApp Business Account keep their original format (named or positional). Used when no channel-specific body is set.
- `smsBody` (string): Channel-specific body for SMS messages. Falls back to `body` if not set.
- `telegramBody` (string): Channel-specific body for Telegram messages. Falls back to `body` if not set.
- `instagramBody` (string): Channel-specific body for Instagram messages. Falls back to `body` if not set.
- `category` (string, required): The category the template is billed under. For a template linked to WhatsApp this is the category **Meta currently assigns**, not the one it was submitted with: Meta can recategorize a template at approval or at any point afterwards, and that changes what the message costs. A recategorization reaches this field through the `template.status_changed` webhook, which carries `data.category`, and through `POST /v1/templates/sync`, which reconciles it against Meta. Read one of those rather than assuming the category you sent is the one in force. For a template that was never submitted to WhatsApp, this is the category stored locally, defaulting to `UTILITY`. One of: `UTILITY`, `MARKETING`, `AUTHENTICATION`.
- `status` (string) One of: `draft`, `pending`, `approved`, `rejected`.
- `variables` (string[]): List of variable names for documentation.
- `headerType` (string): Type of header (text, image, video, document).
- `headerContent` (string): Header content (text or media URL).
- `footer` (string): Footer text for the template.
- `buttons` (object[]): Template buttons.
  - `type` (string) One of: `quick_reply`, `url`, `phone`, `otp`, `request_contact_info`.
  - `text` (string)
  - `url` (string)
  - `example` (string): Sample value used to substitute `{{1}}` in the URL when submitting the template to Meta for review. Only present for dynamic URL buttons.
  - `phoneNumber` (string)
  - `otpType` (string): OTP button type. Required when type is 'otp'. One of: `COPY_CODE`, `ONE_TAP`.
  - `packageName` (string): Android package name. Required for ONE_TAP buttons.
  - `signatureHash` (string): Android app signature hash. Required for ONE_TAP buttons.
- `addSecurityRecommendation` (boolean): Add 'Do not share this code' disclaimer. Only for AUTHENTICATION templates.
- `codeExpirationMinutes` (integer): Code expiration time in minutes. Only for AUTHENTICATION templates.
- `whatsapp` (object): WhatsApp-specific template information.
  - `templateName` (string): WhatsApp template name.
  - `namespace` (string): WhatsApp Business Account namespace.
  - `status` (string): WhatsApp approval status.
- `createdAt` (string<date-time>)
- `updatedAt` (string<date-time>)

### 400

Invalid template.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 401

Unauthorized.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

### 409

A template with this name already exists in the project (`conflict`). Names are unique per project.

- `code` (string, required)
- `message` (string, required)
- `details` (object)

## Example

```bash
curl -X POST "https://api.zavu.dev/v1/templates" \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "order_confirmation",
  "language": "en",
  "body": "Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.",
  "whatsappCategory": "UTILITY",
  "variables": [
    "customer_name",
    "order_id"
  ]
}'
```

```typescript
import Zavudev from "@zavudev/sdk";

const zavu = new Zavudev({ apiKey: process.env.ZAVUDEV_API_KEY });

const result = await zavu.templates.create({
  name: "order_confirmation",
  language: "en",
  body: "Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.",
  whatsappCategory: "UTILITY",
  variables: [
    "customer_name",
    "order_id",
  ],
});

console.log(result);
```
