WhatsAble
API v1Templates

Create a template and submit it to Meta for review

posthttps://api.whatsable.app/v1/templates
Build a request to copy

This page never sends a request. Fill in the fields, then copy the request and run it from your server.

Request
curl -X POST 'https://api.whatsable.app/v1/templates' \
  -H 'Content-Type: application/json' \
  -d '{
  "number_id": "cm2v8a1b20002qz8h9i0j1k2l",
  "name": "order_shipped",
  "display_name": "Order shipped",
  "language": "en_US",
  "category": "utility",
  "header": {
    "type": "image",
    "url": "https://acme.example/box.jpg"
  },
  "body": "Hi {{1}}, your order {{2}} is on its way.",
  "body_examples": [
    "Ana",
    "#1042"
  ],
  "footer": "Reply STOP to stop these messages",
  "buttons": [
    {
      "type": "url",
      "text": "Track",
      "url": "https://acme.example/t/{{1}}",
      "example": "1042"
    },
    {
      "type": "quick_reply",
      "text": "Talk to us"
    }
  ]
}'

Creates a template and submits it to Meta for review.

A 201 means that Meta accepted the template for review, not that Meta approved it. The template starts as pending or approved. A later rejection changes status, and never returns an HTTP error.

A refused request stores no template. A check that fails before the call to Meta submits nothing to Meta.

Authorization

bearerAuth
headerAuthorizationBearer <nfy_live_<32 characters>>

An API key from Settings > API keys, sent as Authorization: Bearer <key>.

Header Parameters

Idempotency-Key?string

Any string up to 255 characters. A repeat with the same key and body answers the stored response; with another body, 409 idempotency_key_reused.

Lengthlength <= 255

Request Body

application/json
  1. body

A template to submit to Meta for review. Every rule here is checked before Meta is called, and a 400 submits nothing.

number_id?string

A number on the WhatsApp Business Account the template is created in. Absent, the key's default number decides.

Length1 <= length <= 64
name?string

Meta's identifier: lowercase letters, digits and underscores, starting with a letter. Derived from display_name when absent.

Match^[a-z][a-z0-9_]*$
Length1 <= length <= 512
display_name?string

The name people read. With name, it must derive to name.

Length1 <= length <= 512
language*string

Meta's language code, such as en_US.

Length2 <= length <= 15
category*string
Value in"marketing""utility""authentication"
header?||||

none (the default), text, or a media header, by url or by media_id, whose file the server uploads to Meta and keeps.

body?string

Up to 1,024 characters, with {{1}}, {{2}}, ... numbered from 1 without gaps. Named variables are not accepted. Absent only for an authentication template.

Lengthlength <= 1024
body_examples?array<>

One example per body variable, in order. Absent, placeholders are submitted; real examples are what Meta's reviewers read.

Itemsitems <= 100
footer?string
Length1 <= length <= 60
buttons?array<||||>
Itemsitems <= 10
authentication?

For the authentication category only.

Response Body

Meta accepted the template for review.

application/json
  1. response

A WhatsApp message template, with the variable schema a send needs.

object*string
id*string
Length1 <= length <= 64
name*string

Meta's identifier: lowercase letters, digits and underscores.

display_name*|

The name people read. Null for a template created outside Whatsable.

language*string

Meta's language code, such as en_US.

category*|

Meta's category, which Meta may change during review.

status*|

Meta's review status. Only approved templates send.

rejected_reason*|

Meta's reason when status is rejected, otherwise null.

waba_id*string

The WhatsApp Business Account the template belongs to. Every number on it can send it.

body*string

The body text, with its {{n}} variables.

parameter_format*|

positional ({{1}}) or named ({{customer_name}}). The API lists a named template but cannot send it yet.

variables*array<>

Every value a send must or may supply. Build a send's template.variables from it.

buttons*array<>
created_at*string
Formatdate-time
updated_at*string
Formatdate-time
Example Request
curl -X POST 'https://api.whatsable.app/v1/templates' \  -H 'Content-Type: application/json' \  -d '{  "number_id": "cm2v8a1b20002qz8h9i0j1k2l",  "name": "order_shipped",  "display_name": "Order shipped",  "language": "en_US",  "category": "utility",  "header": {    "type": "image",    "url": "https://acme.example/box.jpg"  },  "body": "Hi {{1}}, your order {{2}} is on its way.",  "body_examples": [    "Ana",    "#1042"  ],  "footer": "Reply STOP to stop these messages",  "buttons": [    {      "type": "url",      "text": "Track",      "url": "https://acme.example/t/{{1}}",      "example": "1042"    },    {      "type": "quick_reply",      "text": "Talk to us"    }  ]}'
Example Response
{  "object": "template",  "id": "cm2v8t1a00001qz8habcd1234",  "name": "order_shipped",  "display_name": "Order shipped",  "language": "en_US",  "category": "utility",  "status": "approved",  "rejected_reason": null,  "waba_id": "9876543210",  "body": "Hi {{1}}, your order {{2}} is on its way.",  "parameter_format": "positional",  "variables": [    {      "key": "header_image",      "component": "header",      "type": "image",      "required": true,      "example": null    },    {      "key": "1",      "component": "body",      "type": "text",      "required": true,      "example": "Ana"    },    {      "key": "2",      "component": "body",      "type": "text",      "required": true,      "example": "#1042"    },    {      "key": "button_0",      "component": "button",      "type": "text",      "required": true,      "example": "1042"    }  ],  "buttons": [    {      "type": "url",      "text": "Track",      "url": "https://acme.example/t/{{1}}",      "phone": null    }  ],  "created_at": "2026-09-01T12:00:00Z",  "updated_at": "2026-09-02T08:00:00Z"}