Create Template
Submit a WhatsApp message template for Meta review. Sendiee runs strict validation locally before forwarding the request to Meta — you will get a structured 422 TEMPLATE_VALIDATION_FAILED for any preventable issue (sequential variables, missing examples, oversized headers, invalid button mix, etc.). This protects your WhatsApp Business Account quality score, which Meta degrades when templates are repeatedly rejected.
POST/v2.0/templates
Aggressive rate limit on this endpoint
On top of the per-org daily limit, this endpoint is independently capped at 2 templates / minute and 30 templates / day. Hitting either returns
429 TEMPLATE_RATE_LIMITED. The cap exists because rejected submissions damage your WABA reputation.Authentication
Bearer token + brandNumber. Your WABA must be in APPROVED review status — otherwise we return 409 WABA_NOT_REVIEWED without contacting Meta.
Request Parameters
Body Parameters — Common
| Parameter | Type | Description |
|---|---|---|
| brandNumber | string | Your WhatsApp Business phone number registered in Sendiee |
| name | string | Lowercase, max 512 chars, /^[a-z][a-z0-9_]*$/ |
| language | string | Locale code (e.g. en_US, en_GB, hi, pt_BR) |
| category | string | MARKETING | UTILITY | AUTHENTICATION |
header (MARKETING / UTILITY only)
header
| Parameter | Type | Description |
|---|---|---|
| header.format | string | NONE | TEXT | IMAGE | VIDEO | DOCUMENT (default NONE) |
| header.text | string | Required for format=TEXT. Max 60 chars, no emoji, max 1 variable {{1}} |
| header.exampleVariables | string[] | Required when header.text contains {{1}}. Length must equal 1 |
| header.mediaUrl | string | For IMAGE/VIDEO/DOCUMENT — public http(s) URL we download and resumable-upload to Meta on your behalf |
| header.mediaHandle | string | Alternative to mediaUrl: a header_handle obtained from Meta's resumable upload API yourself |
| header.filename | string | Required for format=DOCUMENT (e.g. invoice.pdf) |
body (MARKETING / UTILITY only)
body
| Parameter | Type | Description |
|---|---|---|
| body.text | string | Up to 1024 chars. Variables must be sequential ({{1}}, {{2}} …) with no gaps |
| body.exampleVariables | string[] | One example per body variable (required when body has variables) |
footer (MARKETING / UTILITY, optional)
footer
| Parameter | Type | Description |
|---|---|---|
| footer.text | string | Max 60 chars. No variables allowed |
buttons (MARKETING / UTILITY, optional)
buttons[]
| Parameter | Type | Description |
|---|---|---|
| buttons[].type | string | QUICK_REPLY | URL | PHONE_NUMBER | COPY_CODE | WHATSAPP_CALL |
| buttons[].text | string | Max 25 chars. Required for QUICK_REPLY / URL / PHONE_NUMBER / WHATSAPP_CALL |
| buttons[].url | string | Required for type=URL. May contain {{1}} for dynamic suffix |
| buttons[].exampleUrlSuffix | string | Required when URL has a variable |
| buttons[].phone_number | string | E.164 international phone number, required for type=PHONE_NUMBER |
| buttons[].example | string | Promo code, required for type=COPY_CODE |
Button limits
Total ≤ 10 buttons. Per-type caps: URL ≤ 2, PHONE_NUMBER ≤ 1, COPY_CODE ≤ 1.
auth (AUTHENTICATION only)
auth
| Parameter | Type | Description |
|---|---|---|
| auth.otp_type | string | COPY_CODE | ONE_TAP | ZERO_TAP |
| auth.add_security_recommendation | boolean | Append the standard security tip line to the body |
| auth.code_expiration_minutes | number | 1–90 minutes. Adds a code-expiry footer |
| auth.supported_apps | object[] | Required for ONE_TAP / ZERO_TAP. Each entry: { package_name, signature_hash } |
| auth.zero_tap_terms_accepted | boolean | Must be true for ZERO_TAP |
Code Examples
Marketing template with header media + body variables
curl -X POST https://api.sendiee.com/v2.0/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brandNumber": "917012345678",
"name": "diwali_sale_2026",
"language": "en_US",
"category": "MARKETING",
"header": {
"format": "IMAGE",
"mediaUrl": "https://cdn.acme.com/diwali-banner.png"
},
"body": {
"text": "Hi {{1}}, get a flat 25% off on your favourite range. Use code {{2}} at checkout.",
"exampleVariables": ["Surya", "DIWALI25"]
},
"footer": { "text": "Reply STOP to opt out" },
"buttons": [
{ "type": "URL", "text": "Shop now", "url": "https://acme.com/sale" },
{ "type": "QUICK_REPLY", "text": "Talk to us" }
]
}'Authentication (OTP) template
cURL
curl -X POST https://api.sendiee.com/v2.0/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brandNumber": "917012345678",
"name": "login_otp",
"language": "en_US",
"category": "AUTHENTICATION",
"auth": {
"otp_type": "COPY_CODE",
"add_security_recommendation": true,
"code_expiration_minutes": 10
}
}'Response
{
"success": true,
"message": "Template submitted to Meta for review",
"data": {
"templateDbId": "664a...",
"templateId": 1234567890,
"name": "diwali_sale_2026",
"language": "en_US",
"category": "MARKETING",
"status": "PENDING"
}
}Local validation failure (422)
{
"error": true,
"code": "TEMPLATE_VALIDATION_FAILED",
"message": "One or more template fields failed validation",
"details": [
{
"path": "name",
"code": "NAME_INVALID",
"message": "Name must start with a lowercase letter and contain only [a-z0-9_], max 512 chars"
},
{
"path": "body.exampleVariables",
"code": "BODY_EXAMPLES_MISSING",
"message": "body.exampleVariables must contain exactly 2 entries (one per body variable)"
}
]
}Meta rejection (502)
{
"error": true,
"code": "META_REJECTED",
"message": "Template name already exists",
"details": {
"metaErrorCode": 100,
"metaErrorSubcode": 2388023,
"metaMessage": "Invalid parameter",
"metaUserTitle": "Template name already exists",
"metaUserMsg": "A template with this name and language already exists.",
"metaTraceId": "Aabcdef…"
}
}Template approval is asynchronous
Meta returns the template in
PENDING status. Approval usually takes minutes but can take up to 24 hours. Listen on the whatsapp.template_status_update automation event or poll GET /v2.0/templates.Try It
Try it — API Playground