> For the complete documentation index, see [llms.txt](https://docs.channel360.co.za/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.channel360.co.za/api-usage/manage-templates-through-the-api.md).

# Manage templates through the API

List, create, update and re-sync WhatsApp templates from your own code.

List, create, switch on and off, and re-sync your WhatsApp templates from your own code.

{% hint style="info" %}
Most teams create templates in the Channel360 web app and only **send** them through the API. Use this page if you want to automate template work, or build your own screens.
{% endhint %}

## Before you start

* An API key and your Organisation API URL — see [Authentication](/api-usage/get-started/authentication.md).
* What a template is made of — see [Templates, placeholders and components](/api-usage/concepts/templates-placeholders-and-components.md).

## List your templates

`GET` `/org/<ORG_ID>/templates/list`

{% tabs %}
{% tab title="cURL" %}

```bash
curl "https://channel360.co.za/v1.1/org/<ORG_ID>/templates/list?enabled=true" \
  -H "Authorization: Bearer <API_KEY>"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const response = await fetch(
  "https://channel360.co.za/v1.1/org/<ORG_ID>/templates/list?enabled=true",
  { headers: { Authorization: "Bearer <API_KEY>" } }
);

console.log(await response.json());
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

response = requests.get(
    "https://channel360.co.za/v1.1/org/<ORG_ID>/templates/list",
    params={"enabled": "true"},
    headers={"Authorization": "Bearer <API_KEY>"},
)

print(response.json())
```

{% endtab %}
{% endtabs %}

The response is a JSON **array** of templates, not wrapped in an object.

| Query parameter | What it does                                                                      |
| --------------- | --------------------------------------------------------------------------------- |
| `enabled`       | `true` returns only enabled templates. Any other value returns the disabled ones. |
| `filter`        | Only `configured` is accepted. Any other value returns `400`.                     |

<details>

<summary>Example response</summary>

```json
[
  {
    "tags": { "head": [], "body": [], "buttons": [] },
    "organization": "org_example_123456",
    "language": "en",
    "name": "testing_market",
    "messageTemplateId": "meta_template_example_123",
    "category": "MARKETING",
    "components": [
      { "type": "BODY", "text": "Hi would we are marketing", "buttons": [] }
    ],
    "createdAt": "2025-04-07T09:00:27.933Z",
    "description": "auto-ingested",
    "enabled": true,
    "namespace": "",
    "status": "APPROVED",
    "updatedAt": "2025-07-17T09:14:58.444Z",
    "version": 0,
    "id": "template_example_1"
  }
]
```

</details>

To send a template you need its `name`, `language`, a `status` of `APPROVED`, and the placeholders in its `components`. To update it you need its `id`. [From template to request](/api-usage/send-a-template-notification/filling-template-placeholders.md#from-template-to-request) shows how a listed template becomes a send request.

## Get one template by name

`GET` `/org/<ORG_ID>/templates/<TEMPLATE_NAME>`

```bash
curl "https://channel360.co.za/v1.1/org/<ORG_ID>/templates/order_shipped" \
  -H "Authorization: Bearer <API_KEY>"
```

The path takes the template's **name**, not its ID. If you have the same name in two languages, the first match is returned. If there is no match, the response is `404`.

## Create a template

`POST` `/org/<ORG_ID>/whatsapp/templates`

| Field         | Required | What to put                                                                                     |
| ------------- | -------- | ----------------------------------------------------------------------------------------------- |
| `name`        | Yes      | Lowercase letters, digits and underscores only. Must be unique for its language.                |
| `description` | Yes      | What the template is for.                                                                       |
| `namespace`   | Yes      | Use `"default"`.                                                                                |
| `language`    | Yes      | The language code, such as `en_US`. It must match the template's text, or Meta rejects it.      |
| `enabled`     | Yes      | `true` or `false`.                                                                              |
| `category`    | Yes      | `MARKETING`, `UTILITY` or `AUTHENTICATION`.                                                     |
| `components`  | Yes      | The message structure: header, body, footer and buttons. Rules are in the table below.          |
| `tags`        | No       | Only used by web-app campaigns. See [Tags vs. the API](/api-usage/concepts/tags-vs-the-api.md). |

| Component | Required | Rules                                                                                               |
| --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `HEADER`  | No       | Format `TEXT`, `IMAGE`, `VIDEO` or `DOCUMENT`. Up to 60 characters. At most one placeholder.        |
| `BODY`    | Yes      | Up to 1,024 characters. Can have several placeholders. Needs an `example` when it has placeholders. |
| `FOOTER`  | No       | Up to 60 characters.                                                                                |
| `BUTTONS` | No       | Button text up to 25 characters. At most one placeholder. Needs an `example` when it has one.       |

Media headers need an example file link. Host the file on your own storage at a **public `https://` link** — WhatsApp downloads it from there, so it must open without a login. Media limits: video `video/mp4` (H.264 video, AAC audio), image `image/jpeg` or `image/png`, document `application/pdf` or `text/csv`; 15 MB each.

{% tabs %}
{% tab title="Text only" %}

```bash
curl -X POST "https://channel360.co.za/v1.1/org/<ORG_ID>/whatsapp/templates" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "summer_sale_announcement",
    "description": "Marketing template used to promote seasonal specials",
    "namespace": "default",
    "language": "en_US",
    "enabled": true,
    "category": "MARKETING",
    "components": [
      { "type": "HEADER", "format": "TEXT", "text": "Summer Sale!" },
      { "type": "BODY", "text": "Enjoy our biggest discounts of the season! Shop your favourite items and save up to 40%. Hurry—limited time only." },
      { "type": "FOOTER", "text": "Shop now" }
    ]
  }'
```

{% endtab %}

{% tab title="Placeholder and URL button" %}

```json
{
  "name": "dashboard_link",
  "description": "Link to the customer's dashboard",
  "namespace": "default",
  "language": "en_US",
  "enabled": true,
  "category": "UTILITY",
  "components": [
    {
      "type": "BODY",
      "text": "Hi {{1}}, tap below to view your dashboard.",
      "example": { "body_text": [["Customer"]] }
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "URL",
          "text": "View Dashboard",
          "url": "https://example.com/dashboard/{{1}}",
          "example": ["12345"]
        }
      ]
    }
  ]
}
```

{% endtab %}

{% tab title="Image header" %}

```json
{
  "name": "image_info_template",
  "description": "Image header template example",
  "namespace": "default",
  "language": "en_US",
  "enabled": true,
  "category": "MARKETING",
  "components": [
    {
      "type": "HEADER",
      "format": "IMAGE",
      "example": { "header_url": ["https://via.placeholder.com/600x400.png"] }
    },
    {
      "type": "BODY",
      "text": "Hi {{1}}, here is the image you requested.",
      "example": { "body_text": [["Customer"]] }
    }
  ]
}
```

{% endtab %}

{% tab title="Authentication" %}

```json
{
  "name": "authentication_code_copy_code_button",
  "description": "One-time PIN with a copy-code button",
  "namespace": "default",
  "language": "en",
  "enabled": true,
  "category": "AUTHENTICATION",
  "components": [
    { "type": "BODY", "add_security_recommendation": true },
    { "type": "FOOTER", "code_expiration_minutes": 10 },
    { "type": "BUTTONS", "buttons": [ { "type": "OTP", "otp_type": "COPY_CODE" } ] }
  ]
}
```

Authentication templates have their own rules:

| Field                         | Rule                                                                                   |
| ----------------------------- | -------------------------------------------------------------------------------------- |
| `add_security_recommendation` | Optional. `true` adds the fixed text "For your security, do not share this code."      |
| `code_expiration_minutes`     | Optional. Adds a footer saying when the code expires. Whole minutes, from `1` to `90`. |
| Button `type`                 | Required. Must be `OTP`.                                                               |
| {% endtab %}                  |                                                                                        |
| {% endtabs %}                 |                                                                                        |

A successful request returns `201`:

```json
{ "message": "Template summer_sale_announcement has been created successfully" }
```

The template is `PENDING` until WhatsApp approves or rejects it. Subscribe to the `template:status` webhook event to be told when it changes (see [Receive delivery updates](/api-usage/receive-delivery-updates.md)), or list your templates again.

[Meta's supported languages](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/supported-languages/) · [Meta's template guidelines](https://developers.facebook.com/docs/whatsapp/message-templates/guidelines/)

## Switch templates on or off, or edit their description

**One template:** `PUT` `/org/<ORG_ID>/templates/<TEMPLATE_ID>`. `<TEMPLATE_ID>` is the `id` from the list response.

| Field         | Type    |
| ------------- | ------- |
| `description` | string  |
| `namespace`   | string  |
| `enabled`     | boolean |
| `tags`        | object  |

Send only the fields you want to change. Other fields are ignored.

```bash
curl -X PUT "https://channel360.co.za/v1.1/org/<ORG_ID>/templates/<TEMPLATE_ID>" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'
```

{% hint style="danger" %}
You cannot change a template's components or structure after it is created. Create a new template instead.
{% endhint %}

Updates only affect new campaigns. A campaign that was already created keeps using the original template version.

**Several templates:** `PUT` `/org/<ORG_ID>/templates`

```bash
curl -X PUT "https://channel360.co.za/v1.1/org/<ORG_ID>/templates" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "templateIds": ["template_example_1", "template_example_2"], "enabled": false }'
```

Returns `201` with `{ "message": "Successfully updated templates" }`. IDs that are not in your organisation are skipped.

## Re-sync templates from WhatsApp

`POST` `/org/<ORG_ID>/whatsapp/templates/refresh` — no body.

Use it after creating or editing a template directly in Meta's tools, so Channel360 sees the change.

```bash
curl -X POST "https://channel360.co.za/v1.1/org/<ORG_ID>/whatsapp/templates/refresh" \
  -H "Authorization: Bearer <API_KEY>"
```

```json
{ "message": "Templates Successfully Synced", "data": [[], []] }
```

`data` lists the IDs of templates that were updated and added.

## What can go wrong

| What you see                                        | Why                                                            | What to do                                                                                                                                                    |
| --------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **400** on create, naming a `field`                 | A required field is missing, or `name` has invalid characters. | Fix the named field.                                                                                                                                          |
| **400** `Organization is not connected to Whatsapp` | WhatsApp is not connected for this organisation.               | Contact Channel360 support. Create the template in the web app meanwhile.                                                                                     |
| The template stays `PENDING`                        | WhatsApp has not reviewed it yet.                              | Wait for the `template:status` event.                                                                                                                         |
| The template is `REJECTED`                          | It breaks Meta's rules.                                        | Read [Meta's template guidelines](https://developers.facebook.com/docs/whatsapp/message-templates/guidelines/), then create a corrected copy with a new name. |

## Next

{% content-ref url="/pages/F7sOcPd43S0m5RiC3IXD" %}
[Send a template notification](/api-usage/send-a-template-notification.md)
{% endcontent-ref %}

{% content-ref url="/pages/UAUsKmCKXoQDu5gIwW8Q" %}
[Templates, placeholders and components](/api-usage/concepts/templates-placeholders-and-components.md)
{% endcontent-ref %}
