Skip to main content

Create WABA Template

Agent Skill

Automate this endpoint with an AI agent using the 2chat-whatsapp-waba skill:

npx skills add 2ChatCo/agent-skills -s 2chat-whatsapp-waba

Create a WhatsApp Business API (WABA) message template and submit it to WhatsApp for review.

info

Requires a WhatsApp Business API (WABA) channel connected to your 2Chat account. See How to connect WABA to 2Chat.

Approval is not instant

A new template is created with status PENDING and reviewed by WhatsApp — usually within minutes, occasionally up to 24 hours. It cannot be sent until its status is APPROVED. Follow it with Get WABA Template or subscribe to the whatsapp.waba.template.status.updated webhook.

Some templates — authentication ones in particular — are approved during this call, in which case the response already says APPROVED. Read template.status instead of assuming.

Endpoint

POST https://api.p.2chat.io/open/waba/templates

Authentication

Send your API key in the X-User-API-Key header. See Authentication.

Request body

ParameterDescription
phone_numberYour WABA number in E.164 format: no spaces, with leading plus sign. Required.
nameTemplate name. Lowercase letters, digits and underscores only, up to 512 characters. Required.
languageWhatsApp language code, e.g. en, en_US, es_MX, pt_BR. Required.
categoryMARKETING, UTILITY or AUTHENTICATION. Required. WhatsApp may recategorize the template — read template.category in the response.
bodyMessage body, up to 1024 characters. Variables are written {{1}}, {{2}}, … Required, except for AUTHENTICATION templates, where WhatsApp writes the body for you.
headerOptional object: {"format": "TEXT", "text": "..."}. Header text is limited to 60 characters and at most one variable.
footerOptional footer text, up to 60 characters. No variables.
buttonsOptional array of button objects. See Buttons.
examplesSample values per component: {"body": [...], "header": [...], "button": [...]}. Required for every component that contains variables — one value per variable, in order.
add_security_recommendationAUTHENTICATION only. When true, WhatsApp appends its "do not share this code" line.
code_expiration_minutesAUTHENTICATION only. Between 1 and 90. Adds an expiry line to the footer.
descriptionOptional note stored with the template in 2Chat. Only kept for Gupshup-backed channels.

The examples keys match the params keys of Send WABA Message, so the values you supply here have the same shape as the values you will send later.

Media headers

Headers with an IMAGE, VIDEO or DOCUMENT format are not supported through the API yet and are rejected with a 400. Create those templates from the 2Chat app.

Buttons

Each button is an object with a type:

TypeFieldsLimit
QUICK_REPLYtextUp to 10
URLtext, url, and example when the URL contains {{1}}Up to 2
PHONE_NUMBERtext, phone_numberUp to 1
OTPotp_type (COPY_CODE or ONE_TAP), textExactly 1, AUTHENTICATION only

WhatsApp does not allow QUICK_REPLY buttons to be mixed with URL or PHONE_NUMBER buttons, and allows at most 2 call-to-action buttons in total. ONE_TAP buttons also require package_name and signature_hash.

An AUTHENTICATION template with no buttons gets a COPY_CODE button added automatically.

Invocation

curl --request POST \
--url 'https://api.p.2chat.io/open/waba/templates' \
--header 'Content-Type: application/json' \
--header 'X-User-API-Key: your_api_key_here' \
--data '{
"phone_number": "+5215512345432",
"name": "order_shipped",
"language": "en",
"category": "UTILITY",
"header": { "format": "TEXT", "text": "Order {{1}}" },
"body": "Hi {{1}}, your order {{2}} is on its way and arrives on {{3}}.",
"footer": "2Chat",
"examples": {
"header": ["A-8871"],
"body": ["Ada", "A-8871", "August 12"]
}
}'

Create a template with buttons

{
"phone_number": "+5215512345432",
"name": "summer_promo_2026",
"language": "en",
"category": "MARKETING",
"body": "{{1}}, enjoy 30% off until {{2}}.",
"footer": "Reply STOP to unsubscribe",
"buttons": [
{ "type": "URL", "text": "See offers", "url": "https://shop.example.com/promo/{{1}}" },
{ "type": "PHONE_NUMBER", "text": "Call us", "phone_number": "+5215512345432" }
],
"examples": {
"body": ["Ada", "08/31"],
"button": ["summer"]
}
}

examples.button supplies the sample value for the dynamic URL button, in the same order the dynamic buttons appear.

Create an authentication template

WhatsApp writes the body of authentication templates itself (<VERIFICATION_CODE> is your verification code.), so body, header and footer do not apply — the only things you control are the security line, the expiry and the OTP button.

{
"phone_number": "+5215512345432",
"name": "login_code",
"language": "en",
"category": "AUTHENTICATION",
"add_security_recommendation": false,
"code_expiration_minutes": 30,
"buttons": [
{ "type": "OTP", "otp_type": "COPY_CODE", "text": "Copy Code" }
]
}

The verification code is sent as the first body parameter when you send the template. The example_payload in the response shows exactly where it goes.

Response

Returns 201 Created.

{
"success": true,
"template": {
"uuid": "TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23",
"name": "order_shipped",
"status": "PENDING",
"category": "UTILITY",
"language": "en",
"content": "Hi {{1}}, your order {{2}} is on its way and arrives on {{3}}.",
"description": null,
"components": [
{ "type": "HEADER", "format": "TEXT", "text": "Order {{1}}" },
{ "type": "BODY", "text": "Hi {{1}}, your order {{2}} is on its way and arrives on {{3}}." },
{ "type": "FOOTER", "text": "2Chat" }
],
"quality_score": "UNKNOWN",
"rejection_reason": null,
"updated_at": "2026-08-12T19:44:11Z"
},
"required_params": {
"send_supported": true,
"unsupported_reasons": [],
"body": [
{ "index": 1, "placeholder": "{{1}}", "required": true, "type": "string", "max_length": 1024, "example": "Ada" },
{ "index": 2, "placeholder": "{{2}}", "required": true, "type": "string", "max_length": 1024, "example": "A-8871" },
{ "index": 3, "placeholder": "{{3}}", "required": true, "type": "string", "max_length": 1024, "example": "August 12" }
],
"header": [
{ "index": 1, "placeholder": "{{1}}", "required": true, "type": "string", "max_length": 60, "example": "A-8871" }
],
"button": [],
"counts": { "body": 3, "header": 1, "button": 0 }
},
"example_payload": {
"from_number": "+5215512345432",
"to_number": "<to_number>",
"template_uuid": "TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23",
"params": {
"header": ["A-8871"],
"body": ["Ada", "A-8871", "August 12"]
}
},
"message": "Template submitted to WhatsApp for review. Approval is not instant (usually minutes, up to 24 hours) and the template cannot be sent until its status is APPROVED — poll GET /open/waba/templates/{template_uuid} or subscribe to the whatsapp.waba.template.status.updated webhook."
}
FieldDescription
successtrue when the template was created
templateThe created template, in the same shape as Get WABA Template
template.uuidTemplate UUID. Use it as template_uuid when sending, and to poll the approval status
template.statusPENDING, APPROVED or REJECTED. Only APPROVED templates can be sent
template.categoryThe category WhatsApp assigned. It may differ from the one you requested
template.rejection_reasonWhy WhatsApp rejected the template, once it is REJECTED
required_paramsThe values you must supply when sending, per component
example_payloadA ready-to-use body for Send WABA Message — replace <to_number> and the sample values
messageWhat to do next, based on the returned status

Error responses

{
"error": true,
"error_message": "Payload is invalid: `examples.body` is required when `body` contains placeholders"
}
StatusWhen
400The payload breaks a template rule: invalid name charset, unknown category or language, a component over its length limit, variables not numbered consecutively from {{1}}, a body that starts or ends with a variable, missing or mismatched examples, an unsupported button combination, or a media header
400WhatsApp rejected the template. The message is prefixed with Meta: and carries their explanation — most often a duplicate name
401Missing or invalid API key
402The account has expired
404phone_number is not a WABA number on your account
429Too many templates created recently, or the same name and language is already being created
Creation limits

WhatsApp allows 100 template creations per WABA per hour, and caps how many templates an account can hold in total: 250 until the business portfolio is verified, 6,000 once verified. 2Chat caps API creation below the hourly allowance so an automated integration cannot exhaust it for your whole account, including templates created from the app: 40 creations per hour per WABA number and 100 per hour per 2Chat account. Requests over either cap return 429 — wait a few minutes and retry. If WhatsApp rejects a creation because the account is at its total capacity, delete unused templates first — keeping in mind deleted names stay reserved for 30 days.

After creating a template

  1. Poll Get WABA Template with the returned uuid, or subscribe to the whatsapp.waba.template.status.updated webhook to be told when WhatsApp decides.
  2. Once status is APPROVED, send it with Send WABA Message using the example_payload as a starting point.
  3. If it is REJECTED, read rejection_reason, fix the content and create a new template — a rejected template cannot be edited.

Every successful creation also fires the whatsapp.waba.template.created webhook, so other systems can react to templates created by someone else — including from the 2Chat web app.