Template Management API
Overview
The Template Management API lets you create, delete, update, and query the templates of a WABA, and group templates using custom tags. This document covers two groups of endpoints:
- Template endpoints: Get Template List, Query Template Information, Upload Sample Media File, Create Template, Update Template, Delete Template.
- Tag endpoints: Get Template Tag List, Create Template Tag, Update Template Tag, Delete Template Tag, Set Template Tags. Tags apply within the WABA that the current API key belongs to. They are used only for template management on the EngageLab side: they do not modify WhatsApp template content and do not trigger a re-review by Meta.
Call Validation
The EngageLab REST API uses HTTP Basic authentication: add Authorization to the HTTP header:
Authorization: Basic ${base64_auth_string}
The base64_auth_string above is generated as follows: base64(dev_key:dev_secret)
- The header name is "Authorization", and the value is the base64-encoded "username:password" pair (separated by a colon).
- In the WhatsApp API context, the username is the DevKey and the password is the DevSecret. You can obtain them on the Console - Configuration Management - API Key page.
Get Template List
API Endpoint
GET https://wa.api.engagelab.cc/v1/templates
Request Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| name | String | Optional | Template name. Note that this field uses fuzzy matching. |
| language_code | String | Optional | Template language. See Language Codes. |
| category | String | Optional | Template category. ● AUTHENTICATION: authentication code ● MARKETING: marketing ● UTILITY: service notification |
| status | String | Optional | Template status: Developers mainly need to pay attention to APPROVED/PENDING/REJECTED/DISABLED. |
| tag_id | String | Optional | Tag ID, used to filter templates by tag. Accepted values:ungrouped - return only templates without any tag; case-insensitive |
tag_id has an AND relationship with the other query conditions such as name, language_code, category, and status. Passing multiple tags at once is not currently supported. If the tag_id format is invalid, error code 3002 is returned. If the tag does not exist or does not belong to the current WABA, error code 4001 is returned.
Note: If a tag named "ungrouped" (or its localized equivalent) exists in the WABA, you must pass its numeric tag ID to filter by that tag. Passing ungrouped directly is always treated as "filter templates without any tag".
Request Example
Filter by tag:
GET https://wa.api.engagelab.cc/v1/templates?tag_id=101
Filter templates without any tag:
GET https://wa.api.engagelab.cc/v1/templates?tag_id=ungrouped
Response Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| id | String | Required | Template ID |
| name | String | Required | Template name |
| language | String | Required | Template language. See Language Codes. |
| category | String | Required | Template category. |
| components | Object Array | Required | Components of the template content. See the components object in Create Template. |
| status | String | Required | Template status: Developers mainly need to pay attention to APPROVED/PENDING/REJECTED/DISABLED. |
| tags | Object Array | Required | Tags currently set on the template. Returns an empty array when no tag is set. |
Response Example
// A JSON array in which each object is the information of one template
[
{
"id": "406979728071589", // template ID
"name": "code", // template name
"language": "zh_CN", // template language
"status": "APPROVED", // status; APPROVED means approved and available
"category": "OTP", // category; currently OTP/TRANSACTIONAL/MARKETING are supported
"components": [ // template content; can include HEADER/BODY/FOOTER/BUTTON
{
"type": "HEADER",
"format": "text", // format; text/image/location/video/document are supported, TEXT by default
"text": "Registration code" // text content; required when format is text
},
{
"type": "BODY",
"text": "Your verification code is {{1}}. Please enter it within 5 minutes." // text wrapped in double braces {{}} is a template variable
}
],
"tags": [ // tags set on this template; an empty array when no tag is set
{
"id": "101",
"name": "Logistics notification"
}
]
},
......
]
Query Template Information
API Endpoint
GET https://wa.api.engagelab.cc/v1/templates/{template_id}
Here {template_id} is the ID of the template to query.
Request Parameters
NULL
Request Example
GET https://wa.api.engagelab.cc/v1/templates/406979728071589
Response Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| id | String | Required | Template ID |
| name | String | Required | Template name |
| language | String | Required | Template language. See Language Codes. |
| category | String | Required | Template category. Note: template categories were updated no later than May 1, 2023 to: |
| components | Object Array | Required | Components of the template content. See the components object in Create Template. |
| status | String | Required | Template status: APPROVED, IN_APPEAL, PENDING, REJECTED, PENDING_DELETION, DELETED, DISABLED, PAUSED, LIMIT_EXCEEDED |
| tags | Object Array | Required | Tags currently set on the template. Returns an empty array when no tag is set. |
Response Example
{
"id": "406979728071589", // template ID
"name": "code", // template name
"language": "zh_CN", // template language
"status": "APPROVED", // status; APPROVED means approved and available
"category": "OTP", // category; currently OTP/TRANSACTIONAL/MARKETING are supported
"components": [ // template content; can include HEADER/BODY/FOOTER/BUTTON
{
"type": "HEADER",
"format": "text", // format; text/image/location/video/document are supported, TEXT by default
"text": "Registration code" // text content; required when format is text
},
{
"type": "BODY",
"text": "Your verification code is {{1}}. Please enter it within 5 minutes." // text wrapped in double braces {{}} is a template variable
}
],
"tags": [ // tags set on this template; an empty array when no tag is set
{
"id": "101",
"name": "Logistics notification"
}
]
}
Upload Sample Media File
When creating or editing a template with a media header (image, video, document), Meta requires the media file to be uploaded to Meta's servers first. This API uploads the sample file for the template and returns a handle_id, which you then provide in the header_handle field of the create/edit template endpoint.
API Endpoint
POST https://wa.api.engagelab.cc/v1/media/handles
Request Parameters
Content-Type: multipart/form-data
| Parameter | Type | Option | Description |
|---|---|---|---|
| file | file | Required | Sample media file. Maximum size 20 MB. For format requirements, see Media Message Format Requirements. |
Request Example
POST '/v1/media/handles'
--header 'Authorization: Basic amlndWFuZ2RldjpkZXZfc2VjcmV0'
--form 'file=@"/Users/demo/files/demopic.jpeg"'
Response Parameters
Success Response
| Field | Type | Option | Description |
|---|---|---|---|
| handle_id | String | Required | The file identifier returned by Meta, to be provided in the example.header_handle field when creating or editing a template. |
Response example:
{
"handle_id": "4::aW1hZ2UvanBlZw==:ARb2JGd8LbvJbfmpMASFAlczcn4hxLC6tkwjasjD4WL6_i34tIisq0IdWNFFFj1KwJMRXPU4xwygHSJd4DHu1f19LcBBl2qeb8EuEcgnIUPYIQ:e:1682169041:4985146461608173:100084026087657:ARazr9kxfzKshJE4WpY"
}
Failure Response
The HTTP status code is 4xx or 5xx, and the response body contains the following fields:
| Field | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code |
| message | String | Required | Error details |
Response example:
{
"code": 3002,
"message": "whatsapp.template field must be set correctly when type is template"
}
Create Template
API Endpoint
POST https://wa.api.engagelab.cc/v1/templates
Call Example
{
"name": "template_name", // template name; duplicate names are allowed; only lowercase letters, digits, and underscores are supported
"language": "zh_CN", // template language; two templates with the same name cannot use the same language
"category": "OTP", // category; currently OTP/TRANSACTIONAL/MARKETING are supported
"components": [
{ // template content
"type": "BODY", // content block; currently HEADER/BODY/FOOTER/BUTTONS are supported
"text": "define var as {{1}}" // the text itself; the format field is not required when the body is text
"example": {
"body_text": [
[
"var1"
]
]
}
},
{
"type": "HEADER",
"format": "image", // content type; text/image/video/document/location are supported
"example": {
"header_handle": [
"https://jiguang.cn/demopic.jpg"
]
}
},
{
"type": "FOOTER",
"text": "footer only support text without variable"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "PHONE_NUMBER", // button type; PHONE_NUMBER/URL/QUICK_REPLY are supported
"text": "this is a phone number",
"phone_number": "8613800138000"
}
]
}
]
}
Request Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| name | String | Required | Template name. Only lowercase letters, digits, and underscores are supported, up to 512 characters. |
| language | String | Required | Template language. See Language Codes. |
| category | String | Required | Template category. Note: template categories were updated no later than May 1, 2023 to: |
| components | Object Array | Required | Components that describe the template content. See the components object. Note that a component with type=BODY must be included. |
components Object
This object describes the template content. A template consists of the components "header (HEADER)", "body (BODY)", "footer (FOOTER)", and "buttons (BUTTONS)", specified with type. Different component types support different parameters, as described below.
header Component
The header component as a whole is optional. If you do not need a header, simply omit this component.
| Parameter | Type | Option | Description |
|---|---|---|---|
| type | String | Required | Component type. Value: HEADER |
| format | String | Required | Header format. Values: text, image, video, document — corresponding to text, image, video, and file. |
| text | String | Optional | Header text content. Set this field when format=text. The header text can contain a variable, but only 1 variable is supported, expressed as {{1}}. |
| example | JSON Object | Optional | Header example. Required when text contains a variable or format is a media type. See example object description. |
example Object Description
| Parameter | Type | Option | Description |
|---|---|---|---|
| header_handle | String Array | Optional | Required when format is image, video, or document. This field no longer accepts a media URL; you must pass the handle_id obtained from the Upload Sample Media File API. |
| header_text | String Array | Optional | When format is text and contains a variable, pass the replacement value of that variable in this field. For example: "header_text": ["var1"] |
body Component
The body component is required; you must set the body content.
| Parameter | Type | Option | Description |
|---|---|---|---|
| type | String | Required | Component type. Value: BODY |
| text | String | Required | Body content, up to 1024 characters. Multiple variables are supported. A variable consists of double braces plus the variable index; indexes must start from 1 and increase, such as {{1}} and {{2}}. |
| example | JSON Object | Optional | Body example. Meta reviewers use the example to judge whether your message is compliant. See example object description. Required when text contains variables. |
example Object Description
| Parameter | Type | Option | Description |
|---|---|---|---|
| body_text | String Array | Optional | When text contains variables, pass the replacement values of all variables in this field, in the order of the variable indexes. For example: "body_text": [["var1","var2","var3"]] |
footer Component
The footer component as a whole is optional. If you do not need a footer, simply omit this component.
| Parameter | Type | Option | Description |
|---|---|---|---|
| type | String | Required | Component type. Value: FOOTER |
| text | String | Required | Footer content. Plain text only; variables are not allowed. |
buttons Component
The buttons component as a whole is optional. If you do not need buttons, simply omit this component.
| Parameter | Type | Option | Description |
|---|---|---|---|
| type | String | Required | Component type. Value: BUTTONS |
| buttons | Object Array | Required | Button information. See buttons object description. |
buttons Object Description
| Parameter | Type | Option | Description |
|---|---|---|---|
| type | String | Required | Button type. Values: QUICK_REPLY, URL, PHONE_NUMBER — corresponding to quick reply, visit website, and call phone number. |
| text | String | Required | The text shown on the button. Variables are not allowed; plain text only, up to 25 characters. |
| url | String | Optional | Required when type=URL. You can place a variable at the end of the URL; only 1 variable is supported, expressed as {{1}}. |
| phone_number | String | Optional | Required when type=PHONE_NUMBER. Variables are not allowed. The value is a phone number including the international dialing code. |
| example | String Array | Optional | Required when type=QUICK_REPLY and type=URL. For example: "example": [" https://www.website.com/dynamic-url-example"] |
Notes on Authentication Templates
Considerations
For templates in the authentication category (that is, AUTHENTICATION):
- Do not set a HEADER component in components.
- The template content text is localized automatically according to the template's language field.
- For the ONE_TAP mode that opens an app, only Android apps are currently supported, and you must implement the corresponding handshake in your app. For detailed instructions, read the official documentation - Authentication templates.
- The parameter fields submitted when creating a template differ from the template fields recorded on the WhatsApp side after creation. In essence, WhatsApp replaces the BODY, FOOTER, and BUTTONS of templates in this category, so pay special attention when sending template messages: you need to add the button variable. For details, see the Send Message API documentation.
COPY_CODE Example
Submitted data:
{
"name": "copycodetmpl",
"language": "zh_CN",
"category": "AUTHENTICATION",
"components": [
{
// body is required
"type": "BODY",
"add_security_recommendation": true // whether to add the security recommendation text
},
{
// footer is optional
"type": "FOOTER",
"code_expiration_minutes": 2 // adds the expiration time display, range [1,90]; omit this field if not needed
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "OTP",
"otp_type": "copy_code",
"text": "copy it" // length limit 25 characters
}
]
}
]
}
The template content actually stored on the WhatsApp side after successful creation:
{
"name": "copycodetmpl",
"language": "zh_CN",
"category": "AUTHENTICATION",
"components": [
{
"type": "BODY",
"text": "*{{1}}* is your verification code. For your security, do not share this code.",
"example": {
"body_text": [
["123456"]
]
}
},
{
"type": "FOOTER",
"text": "This code expires in 2 minutes."
},
{
"type": "BUTTONS",
"buttons": [{
"type": "URL",
"text": "Copy code",
"url": "https://www.whatsapp.com/otp/code/?otp_type=COPY_CODE&code=otp{{1}}",
"example": [
"https://www.whatsapp.com/otp/code/?otp_type=COPY_CODE&code=otp123456"
]
}]
}
]
}
ONE_TAP Example
Submitted data:
{
"name": "copycodetmpl",
"language": "zh_CN",
"category": "AUTHENTICATION",
"components": [
{
// body is required
"type": "BODY",
"add_security_recommendation": true // whether to add the security recommendation text
},
{
// footer is optional
"type": "FOOTER",
"code_expiration_minutes": 2 // adds the expiration time display, range [1,90]; omit this field if not needed
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "OTP",
"otp_type": "one_tap",
"text": "auto1", // length limit 25 characters
"autofill_text": "auto1", // length limit 25 characters
"package_name": "ppssd",
"signature_hash": "asds"
}
]
}
]
}
The template content actually stored on the WhatsApp side after successful creation:
{
"name": "copycodetmpl",
"language": "zh_CN",
"category": "AUTHENTICATION",
"components": [
{
"type": "BODY",
"text": "*{{1}}* is your verification code. For your security, do not share this code.",
"example": {
"body_text": [
["123456"]
]
}
},
{
"type": "FOOTER",
"text": "This code expires in 2 minutes."
},
{
"type": "BUTTONS",
"buttons": [{
"type": "URL",
"text": "copy1",
"url": "https://www.whatsapp.com/otp/code/?otp_type=ONE_TAP&cta_display_name=auto1&package_name=ppssd&signature_hash=asds&code=otp{{1}}",
"example": ["https://www.whatsapp.com/otp/code/?otp_type=ONE_TAP&cta_display_name=auto1&package_name=ppssd&signature_hash=asds&code=otp123456"]
}]
}
]
}
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| template_id | String | Required | Template ID, returned on success |
{
"template_id": "1275172986566180" // template ID
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
{
"code": 5002,
"message": "Invalid parameter. code:100:2388042"
}
Update Template
API Endpoint
PUT https://wa.api.engagelab.cc/v1/templates/{templateId}
Call Example
{
"components": [{ // template content
"type": "BODY", // content block
"text": "define var as {{1}}",
"example": {
"body_text": [["var1"]]
}
},{
"type": "HEADER",
"format": "image", // content type: image/video/document
"example": {
// Note: you must provide the handle_id returned by the upload endpoint; an image URL is no longer supported
"header_handle": ["4::aW1hZ2UvanBlZw==:ARb2JGd8LbvJbfmpMASFAlcz..."]
}
},{
"type": "FOOTER",
"text": "footer only support text without variable"
},{
"type": "BUTTONS",
"buttons": [{
"type": "PHONE_NUMBER",
"text": "this is a phone number",
"phone_number": "8613800138000"
}]
}]
}
Request Parameters
Same as the Request Parameters of the Create Template endpoint.
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Response code, always 0 |
| message | String | Required | Response message, always success |
{
"code": 0,
"message": "success"
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
{
"code": 5002,
"message": "Invalid parameter. code:100:2593002"
}
Delete Template
API Endpoint
DELETE https://wa.api.engagelab.cc/v1/templates/{template_name}
Note: what you pass here is the template name, not the template ID. All language versions of the template with this name will be deleted.
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Response code, always 0 |
| message | String | Required | Response message, always success |
{
"code": 0,
"message": "success"
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
{
"code": 2004,
"message": "something error"
}
Get Template Tag List
Returns all tags under the WABA that the current API key belongs to, without pagination.
API Endpoint
GET https://wa.api.engagelab.cc/v1/template-tags
Request Parameters
NULL
Request Example
GET https://wa.api.engagelab.cc/v1/template-tags
Response Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| id | String | Required | Tag ID |
| name | String | Required | Tag name |
| template_count | Integer | Required | The number of templates in the current WABA that have this tag. Templates with the same name in different languages are counted separately by template ID. |
Response Example
[
{
"id": "101",
"name": "Logistics notification",
"template_count": 3
},
{
"id": "102",
"name": "After-sales support",
"template_count": 0
}
]
An empty array [] is returned when the WABA has no tags.
Create Template Tag
API Endpoint
POST https://wa.api.engagelab.cc/v1/template-tags
Request Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| name | String | Required | Tag name, 1–64 characters. For naming requirements, see Tag Naming Rules. |
Request Example
{
"name": "Logistics notification"
}
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| id | String | Required | Tag ID |
| name | String | Required | The normalized tag name |
{
"id": "101",
"name": "Logistics notification"
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
{
"code": 3003,
"message": "template tag name already exists"
}
Update Template Tag
API Endpoint
PUT https://wa.api.engagelab.cc/v1/template-tags/{tag_id}
Here {tag_id} is the ID of the tag to update.
Request Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| name | String | Required | The new tag name, 1–64 characters. For naming requirements, see Tag Naming Rules. |
Request Example
{
"name": "After-sales support"
}
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| id | String | Required | Tag ID |
| name | String | Required | The updated tag name |
{
"id": "101",
"name": "After-sales support"
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
{
"code": 4001,
"message": "template tag not found"
}
Delete Template Tag
API Endpoint
DELETE https://wa.api.engagelab.cc/v1/template-tags/{tag_id}
Note: deleting a tag only removes the association between templates and that tag. It does not delete the templates and does not affect message sending.
Here {tag_id} is the ID of the tag to delete.
Request Parameters
NULL
Request Example
DELETE https://wa.api.engagelab.cc/v1/template-tags/101
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| affected_template_count | Integer | Required | The number of templates disassociated by this operation. Templates with the same name in different languages are counted separately by template ID. |
{
"affected_template_count": 3
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
{
"code": 4001,
"message": "template tag not found"
}
Set Template Tags
API Endpoint
PUT https://wa.api.engagelab.cc/v1/templates/{template_id}/tags
Note: this endpoint performs a full overwrite. tag_ids is the complete set of tags the template will have after saving; any existing tag not included will be disassociated.
Here {template_id} is the ID of the template whose tags you want to set.
Request Parameters
| Parameter | Type | Option | Description |
|---|---|---|---|
| tag_ids | String Array | Required | The complete set of tag IDs the template will have after saving. It must be provided explicitly and cannot be null. All IDs must belong to the current WABA; duplicate IDs are deduplicated automatically. |
Notes on tag_ids:
- Passing
[]clears all tags of the template. - If tag_ids is omitted or
null, the request fails and the existing tags are not cleared. - When the request fails, the template's tag set remains unchanged, so you can simply retry.
- There is no limit on the number of tags per template; you can pass all tags of the current WABA.
Request Example
{
"tag_ids": ["101", "102"]
}
Response Parameters
Success Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Response code, always 0 |
| message | String | Required | Response message, always success |
{
"code": 0,
"message": "success"
}
Failure Response
| Parameter | Type | Option | Description |
|---|---|---|---|
| code | int | Required | Error code, returned on failure |
| message | String | Required | Error message, returned on failure |
The template does not exist or does not belong to the current WABA:
{
"code": 4001,
"message": "template not found"
}
The tag does not exist or does not belong to the current WABA:
{
"code": 4001,
"message": "template tag not found"
}
tag_ids is omitted or null:
{
"code": 3002,
"message": "template tag IDs must be provided as an array"
}
Error Codes
"Tag endpoints" in the table below refers to the five tag endpoints listed in the Overview, and also covers the case of filtering by tag_id in Get Template List.
| Error code | HTTP code | Applicable endpoints | Description |
|---|---|---|---|
| 1000 | 500 | All endpoints | Internal error |
| 2001 | 401 | All endpoints | Authentication failed on the EngageLab side: no token with a valid data format was provided |
| 2002 | 401 | All endpoints | Authentication failed on the EngageLab side: the token has expired or has been disabled |
| 2003 | 400 | All endpoints | Authentication failed on the WhatsApp side. Please contact EngageLab customer service. |
| 2004 | 403 | All endpoints | No permission to call this API, or the related account or WABA has been disabled |
| 3001 | 400 | All endpoints | Invalid request parameter format. Check that JSON format is used and that field types meet the requirements. |
| 3002 | 400 | All endpoints | Invalid request parameters. Check that the request parameters meet the requirements. |
| 3002 | 400 | Tag endpoints | The tag name is empty |
| 3002 | 400 | Tag endpoints | The tag name exceeds 64 characters. See Tag Naming Rules. |
| 3002 | 400 | Tag endpoints | The tag name contains disallowed characters. See Tag Naming Rules. |
| 3002 | 400 | Tag endpoints | Invalid tag ID format; it must be a string of positive integers |
| 3002 | 400 | Tag endpoints | tag_ids was not provided, or its value was null, when setting template tags |
| 3003 | 400 | All endpoints | Invalid request parameters: the related business validation failed |
| 3003 | 400 | Tag endpoints | A tag with the same name already exists in the WABA. Duplicate checking is case- and accent-insensitive. |
| 3003 | 400 | Tag endpoints | The WABA has reached the limit of 20 tags |
| 3003 | 400 | Tag endpoints | Tag operations are busy. Simply retry later; retrying will not create duplicate data. |
| 4001 | 400 | All endpoints | The template does not exist or does not belong to the current WABA |
| 4001 | 400 | Tag endpoints | The tag does not exist or does not belong to the current WABA |
| 5002 | 400 | All endpoints | The template request failed on Meta's side. See the error description in the message field for details. |
Notes
Media Message Format Requirements
| Media type | Supported Content-Type | Size limit |
|---|---|---|
| image | image/jpeg, image/png; transparent backgrounds are not supported | 5 MB |
| video | video/mp4 | 16MB |
| document | PDF only | 100 MB |
Tag Naming Rules
When creating and updating tags, the server first normalizes the name, then validates its length and checks for duplicates.
Normalization: leading and trailing whitespace is removed, and consecutive whitespace characters within the name are collapsed into a single space. For example, if you submit " Logistics notification ", the name actually stored and returned is "Logistics notification".
Character restrictions: spaces, underscores, hyphens, visible characters of any language, and emoji are allowed; line breaks, tabs, control characters, and invisible formatting characters are not allowed.
Length: after normalization, the name must be 1–64 characters. Length is counted in Unicode code points, and one emoji may occupy several code points.
Duplicate checking: names must be unique within a WABA. Duplicate checking is case- and accent-insensitive — for example, Logistics, logistics, and Logístics are treated as the same name. There are no reserved words.
Tag Usage Limits
- A single WABA can have at most 20 tags.
- There is no limit on the number of tags per template; you can assign all existing tags of the current WABA, so the effective limit is 20.
- Tag IDs are strings in both requests and responses (for example,
"101"). Do not parse them as numbers. - Templates with the same name in different languages have their tags set independently by template ID. For example, the Chinese and English versions of the same template must be set separately.
- Tags are not written to Meta. They do not change the template status or quality rating and do not trigger a re-review.
Language Codes
| Language | Code |
|---|---|
| Afrikaans | af |
| Albanian | sq |
| Arabic | ar |
| Azerbaijani | az |
| Bengali | bn |
| Bulgarian | bg |
| Catalan | ca |
| Chinese (Mainland China) | zh_CN |
| Chinese (Hong Kong) | zh_HK |
| Chinese (Taiwan) | zh_TW |
| Croatian | hr |
| Czech | cs |
| Danish | da |
| Dutch | nl |
| English | en |
| English (UK) | en_GB |
| English (US) | en_US |
| Estonian | et |
| Filipino | fil |
| Finnish | fi |
| French | fr |
| Georgian | ka |
| German | de |
| Greek | el |
| Gujarati | gu |
| Hausa | ha |
| Hebrew | he |
| Hindi | hi |
| Hungarian | hu |
| Indonesian | id |
| Irish | ga |
| Italian | it |
| Japanese | ja |
| Kannada | kn |
| Kazakh | kk |
| Kinyarwanda | rw_RW |
| Korean | ko |
| Kyrgyz | ky_KG |
| Lao | lo |
| Latvian | lv |
| Lithuanian | lt |
| Macedonian | mk |
| Malay | ms |
| Malayalam | ml |
| Marathi | mr |
| Norwegian | nb |
| Persian | fa |
| Polish | pl |
| Portuguese (Brazil) | pt_BR |
| Portuguese (Portugal) | pt_PT |
| Punjabi | pa |
| Romanian | ro |
| Russian | ru |
| Serbian | sr |
| Slovak | sk |
| Slovenian | sl |
| Spanish | es |
| Spanish (Argentina) | es_AR |
| Spanish (Spain) | es_ES |
| Spanish (Mexico) | es_MX |
| Swahili | sw |
| Swedish | sv |
| Tamil | ta |
| Telugu | te |
| Thai | th |
| Turkish | tr |
| Ukrainian | uk |
| Urdu | ur |
| Uzbek | uz |
| Vietnamese | vi |
| Zulu | zu |
You can also download this file to view the mapping between languages and their codes:
Template language codes.xlsx










