# Templates

> Manage WhatsApp message templates

Source: https://docs.soosh.io/reference/api/templates/

## Overview

WhatsApp Message Templates are pre-approved message formats required for sending business-initiated messages. Templates must be approved by Meta before use.

## List Templates

Retrieve all templates for your account.

`GET /api/templates`

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `page` | integer | Page number (default: 1) |
| `limit` | integer | Page size, 1–100. Default `50`. |
| `status` | string | Filter by status (APPROVED, PENDING, REJECTED) |
| `category` | string | Filter by category (MARKETING, UTILITY, AUTHENTICATION) |
| `account` | string | Filter by WhatsApp account **name** |
| `search` | string | Case-insensitive match on `name` or `display_name` |

Ordered by `created_at DESC`.

> **Careful:** Soosh stores templates as **flat fields** (`header_type`, `header_content`, `body_content`,
> `footer_content`, `buttons`, `sample_values`) — not as Meta's nested `components` array. The
> account is referenced by name via `whatsapp_account`; there is no `account_id`, and the filter
> parameter is `account`.

### Response

```json
{
  "status": "success",
  "data": {
    "templates": [
      {
        "id": "uuid",
        "whatsapp_account": "Main Business",
        "meta_template_id": "123456789",
        "name": "order_confirmation",
        "display_name": "Order Confirmation",
        "language": "en",
        "category": "UTILITY",
        "status": "APPROVED",
        "header_type": "IMAGE",
        "header_content": "",
        "body_content": "Hi {{1}}, your order #{{2}} has been confirmed!",
        "footer_content": "Thank you for shopping with us",
        "buttons": [
          { "type": "URL", "text": "Track Order", "url": "https://example.com/track/{{1}}" }
        ],
        "sample_values": [],
        "add_security_recommendation": false,
        "code_expiration_minutes": 0,
        "quality_rating": "UNKNOWN",
        "created_by_name": "Jane Admin",
        "updated_by_name": "Jane Admin",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z"
      }
    ],
    "total": 50,
    "page": 1,
    "limit": 50
  }
}
```

## Get Template

Retrieve a single template by ID. Returns the same object shape as a list item.

`GET /api/templates/{id}`

## Create Template

Create a new template locally. Creating does **not** submit it to Meta — call
[Submit Template](#submit-template) for that.

`POST /api/templates`

### Request Body

```json
{
  "whatsapp_account": "Main Business",
  "name": "welcome_message",
  "display_name": "Welcome Message",
  "language": "en",
  "category": "MARKETING",
  "header_type": "TEXT",
  "header_content": "Welcome!",
  "body_content": "Welcome to our store, {{1}}! Use code {{2}} for 10% off your first order.",
  "footer_content": "Reply STOP to unsubscribe",
  "buttons": [],
  "sample_values": ["John", "SAVE10"]
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `whatsapp_account` | string | Yes | Account **name** |
| `name` | string | Yes | Meta template name (lowercase, underscores) |
| `language` | string | Yes | Language code, e.g. `en` |
| `category` | string | Yes | `MARKETING`, `UTILITY`, or `AUTHENTICATION` |
| `display_name` | string | No | Friendly label shown in the UI |
| `header_type` | string | No | `TEXT`, `IMAGE`, `DOCUMENT`, `VIDEO`, or `NONE` |
| `header_content` | string | No | Header text when `header_type` is `TEXT` |
| `body_content` | string | No | Body text with `{{1}}`-style or named placeholders |
| `footer_content` | string | No | Footer text |
| `buttons` | array | No | Button definitions |
| `sample_values` | array | No | Example values Meta uses during review |
| `add_security_recommendation` | boolean | No | AUTHENTICATION templates — appends Meta's security line |
| `code_expiration_minutes` | integer | No | AUTHENTICATION templates — 1–90; `0` omits the expiry footer |

## Update Template

Update an existing template. Accepts the same body as Create.

`PUT /api/templates/{id}`

## Delete Template

Delete a template.

`DELETE /api/templates/{id}`

## Sync Templates

Pull templates from Meta for one account and upsert them locally (matching on account + name +
language, restoring soft-deleted rows). Requires `templates:sync`.

`POST /api/templates/sync`

The account name can be given either as an `?account=` query parameter or in the body:

```json
{
  "whatsapp_account": "Main Business"
}
```

Omitting it returns `400 whatsapp_account is required`; an unknown name returns `404`. If Meta
cannot be reached, the endpoint returns `502`.

## Submit Template

Submit a template for Meta approval.

`POST /api/templates/{id}/publish`

### Response

```json
{
  "status": "success",
  "data": {
    "id": "uuid",
    "status": "PENDING"
  }
}
```

## Upload Template Media

Upload a media asset (image, video, or PDF) for a template's media header. This
performs a resumable upload to Meta and returns a **handle** that you embed in
the `HEADER` component's example when creating the template.

`POST /api/templates/upload-media`

Send the request as `multipart/form-data`:

| Field | Location | Required | Description |
|-------|----------|----------|-------------|
| `account` | form field or `?account=` query | Yes | Name of the WhatsApp account. The account must have an `app_id` configured. |
| `file` | form file | Yes | The media file to upload (image/jpeg, image/png, video/mp4, or application/pdf). |

```bash
curl -X POST "http://your-server:8080/api/templates/upload-media?account=main" \
  -H "X-API-Key: soosh_your_api_key" \
  -F "file=@header.jpg"
```

### Response

```json
{
  "status": "success",
  "data": {
    "handle": "4::aW1hZ2UvanBlZw==:...",
    "filename": "header.jpg",
    "mime_type": "image/jpeg",
    "size": 24576
  }
}
```

Set the returned `handle` as `header_content` when creating a template whose `header_type` is
`IMAGE`, `VIDEO` or `DOCUMENT` — Soosh wraps it into Meta's
`example.header_handle` on submission:

```json
{
  "header_type": "IMAGE",
  "header_content": "4::aW1hZ2UvanBlZw==:..."
}
```

> **Careful:** The WhatsApp account must have `app_id` set in its settings; otherwise the
> upload is rejected.

## Template Parts

Meta's four components map onto flat fields in the Soosh API:

| Meta component | Soosh fields |
|-----------|-------------|
| `HEADER` | `header_type` (`TEXT` \| `IMAGE` \| `DOCUMENT` \| `VIDEO` \| `NONE`) + `header_content` |
| `BODY` | `body_content` |
| `FOOTER` | `footer_content` |
| `BUTTONS` | `buttons` |

Soosh assembles the Meta `components` payload itself when you submit the template.

## Template Variables

### Positional Parameters

Use `{{1}}`, `{{2}}`, etc. for dynamic content:

```
Hello {{1}}, your order #{{2}} is ready for pickup at {{3}}.
```

Provide sample values as objects keyed by `component` + `index`:

```json
{
  "body_content": "Hello {{1}}, your order #{{2}} is ready!",
  "sample_values": [
    { "component": "body", "index": 1, "value": "John" },
    { "component": "body", "index": 2, "value": "12345" }
  ]
}
```

### Named Parameters

Templates also support named parameters for better readability:

```
Hello {{customer_name}}, your order #{{order_id}} is ready for pickup at {{store_location}}.
```

When using named parameters, provide sample values with `param_name`:

```json
{
  "body_content": "Hello {{customer_name}}, your order #{{order_id}} is ready!",
  "sample_values": [
    { "component": "body", "param_name": "customer_name", "value": "John" },
    { "component": "body", "param_name": "order_id", "value": "12345" }
  ]
}
```

You do not set `parameter_format` yourself — Soosh detects non-numeric placeholders and sends
`parameter_format: "NAMED"` to Meta on submission.

### URL Button Parameters

Templates can include URL buttons with dynamic variables. For example, a "Track Order" button with URL `https://example.com/track/{{1}}`:

```json
{
  "type": "BUTTONS",
  "buttons": [
    {
      "type": "URL",
      "text": "Track Order",
      "url": "https://example.com/track/{{1}}",
      "example": "https://example.com/track/12345"
    }
  ]
}
```

When sending a template with dynamic URL buttons, provide the dynamic values via the `button_params` field in the [Send Template Message](https://docs.soosh.io/reference/api/messages/#send-template-message) endpoint. The key is the zero-based button index:

```json
{
  "template_name": "order_shipped",
  "template_params": { "1": "John", "2": "12345" },
  "button_params": { "0": "12345" }
}
```

> **Note:** Soosh automatically detects if your template uses named parameters (non-numeric placeholders) and sets the appropriate format when submitting to Meta.

> **Careful:** Template approval can take from a few minutes to 24 hours. Rejected templates must be modified and resubmitted.
