Get WABA Template by UUID
Automate this endpoint with an AI agent using the 2chat-whatsapp-waba skill:
npx skills add 2ChatCo/agent-skills -s 2chat-whatsapp-waba
Retrieve a single WhatsApp Business API (WABA) message template by its UUID, together with the variables a caller must fill in to send it via Send WABA Message and a ready-to-copy example request body.
This is the endpoint to call when you need to know a template's variables before attempting a send — for example to build the input fields of a Zapier, Make or n8n step, or to render a dynamic form.
Requires a WhatsApp Business API (WABA) channel connected to your 2Chat account. See How to connect WABA to 2Chat.
Endpoint
GET https://api.p.2chat.io/open/waba/templates/{template_uuid}
Authentication
Send your API key in the X-User-API-Key header. See Authentication.
Path parameters
| Parameter | Description | Example |
|---|---|---|
template_uuid | UUID of the template. Obtain it from Get WABA Templates (the uuid field). Must start with TMP. Required. | TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23 |
Query parameters
| Parameter | Description | Example |
|---|---|---|
fields_format | Set to flat to additionally receive a fields array with one ready-to-render form field per template variable. Any other value returns a 400. Optional. | flat |
Invocation
- cURL
- Python
- JavaScript
- PHP
curl --request GET \
--url 'https://api.p.2chat.io/open/waba/templates/TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23' \
--header 'X-User-API-Key: your_api_key_here'
import requests
template_uuid = "TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23"
url = f"https://api.p.2chat.io/open/waba/templates/{template_uuid}"
headers = {
"X-User-API-Key": "your_api_key_here"
}
response = requests.get(url, headers=headers)
data = response.json()
# Ready-to-send body: fill in to_number and the placeholder values
print(data["example_payload"])
const axios = require('axios');
const templateUuid = 'TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23';
axios.get(`https://api.p.2chat.io/open/waba/templates/${templateUuid}`, {
headers: {
'X-User-API-Key': 'your_api_key_here'
}
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
<?php
$templateUuid = 'TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23';
$ch = curl_init("https://api.p.2chat.io/open/waba/templates/{$templateUuid}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-User-API-Key: your_api_key_here'
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
{
"success": true,
"template": {
"uuid": "TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23",
"name": "order_shipped",
"status": "APPROVED",
"category": "UTILITY",
"language": "en_US",
"content": "Hi {{1}}, your order {{2}} is on its way.",
"description": null,
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, your order {{2}} is on its way.",
"example_values": ["Ana", "A-4821"],
"sample_text": "Hi Ana, your order A-4821 is on its way."
},
{
"type": "FOOTER",
"text": "Logistics team"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Track order",
"url": "https://example.com/track/{{1}}"
}
]
}
],
"quality_score": "GREEN",
"rejection_reason": null,
"updated_at": "2026-07-28T11:04:52Z"
},
"required_params": {
"send_supported": true,
"unsupported_reasons": [],
"body": [
{
"index": 1,
"placeholder": "{{1}}",
"required": true,
"type": "string",
"max_length": 1024,
"example": "Ana"
},
{
"index": 2,
"placeholder": "{{2}}",
"required": true,
"type": "string",
"max_length": 1024,
"example": "A-4821"
}
],
"header": [],
"header_media_url": null,
"header_media_filename": null,
"button": [
{
"index": 1,
"required": true,
"type": "URL",
"button_text": "Track order",
"url_template": "https://example.com/track/{{1}}",
"dynamic": true,
"placeholder_index": 1,
"placeholder": "{{1}}"
}
],
"counts": {
"body": 2,
"header": 0,
"button": 1
}
},
"example_payload": {
"from_number": "+15550001111",
"to_number": "<to_number>",
"template_uuid": "TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23",
"params": {
"body": ["Ana", "A-4821"],
"button": ["<button_1>"]
}
}
}
Top-level fields
| Field | Description |
|---|---|
success | true when the request succeeded |
template | The template itself, in the same shape used by Get WABA Templates |
required_params | The params.* values you must supply when sending this template |
example_payload | A ready-to-copy request body for Send WABA Message |
fields | Only present with ?fields_format=flat. Flat list of form fields — see Flat fields format |
template
| Field | Description |
|---|---|
uuid | Template UUID |
name | Template name (lowercase with underscores) |
status | Meta approval status: APPROVED, PENDING, REJECTED, FAILED, … |
category | Template category (e.g. UTILITY, MARKETING, AUTHENTICATION) |
language | Template language code (e.g. en_US, es_MX) |
content | The body text, including placeholders such as {{1}} |
description | Optional template description, or null |
components | Normalized component list: HEADER (with format and, for media headers, default_media_url), BODY (with text, optional example_values and sample_text), FOOTER, and BUTTONS |
quality_score | Meta quality rating (GREEN, YELLOW, RED, or UNKNOWN) |
rejection_reason | Reason Meta rejected the template, when applicable, otherwise null |
updated_at | Last update time (ISO 8601, UTC) |
required_params
| Field | Description |
|---|---|
send_supported | false when the template uses a shape Send WABA Message cannot handle (e.g. more than one dynamic URL button, or quick replies mixed with call-to-action buttons) |
unsupported_reasons | Human-readable explanations for send_supported: false. Empty array when the template is supported |
body | One descriptor per body placeholder, in ascending placeholder order. Maps to params.body |
header | One descriptor per placeholder of a TEXT header. Maps to params.header. Empty for media or absent headers |
header_media_url | Present when the header is IMAGE, VIDEO or DOCUMENT: { "required": true, "format": "IMAGE", "default_media_url": "…" }. Maps to params.header_media_url. null otherwise |
header_media_filename | { "required": false } for DOCUMENT headers, so you can override the filename shown to the recipient. Maps to params.header_media_filename. null otherwise |
button | One descriptor per button that consumes a runtime value — in practice, dynamic URL buttons. Maps to params.button. Quick replies, phone-number buttons and the OTP button of an AUTHENTICATION template take no value and are not listed here (they are still visible under template.components) |
counts | How many values each params.* array must carry: { "body": n, "header": n, "button": n } |
Each descriptor in body and header carries:
| Field | Description |
|---|---|
index | Placeholder number as it appears in the template |
placeholder | The literal placeholder, e.g. {{1}} |
required | Always true — WhatsApp rejects a send with missing placeholder values |
type | Value type. Always string |
max_length | Maximum characters accepted: 1024 for body values, 60 for header values |
example | Sample value taken from the approved template, or null when the provider did not supply one |
Each descriptor in button carries index, type, button_text, url_template, dynamic, placeholder_index, placeholder and required.
params.body, params.header and params.button are arrays, filled in the same order the descriptors are returned — not maps keyed by placeholder number. A template whose body uses only {{2}} and {{3}} still takes a two-element params.body array.
Flat fields format
Add ?fields_format=flat when you want one ready-to-render input per variable instead of walking the nested body / header / button structure — useful for Zapier and Make field builders or dynamic forms.
curl --request GET \
--url 'https://api.p.2chat.io/open/waba/templates/TMP1b44a079-c75c-4403-bc8f-a75c4ce5cd23?fields_format=flat' \
--header 'X-User-API-Key: your_api_key_here'
The response is identical, plus a fields array:
{
"fields": [
{
"name": "body_1",
"label": "Body {{1}}",
"type": "text",
"required": true,
"help": "e.g. Ana"
},
{
"name": "body_2",
"label": "Body {{2}}",
"type": "text",
"required": true,
"help": "e.g. A-4821"
},
{
"name": "button_1",
"label": "Button \"Track order\" — URL variable",
"type": "text",
"required": true
}
]
}
| Field | Description |
|---|---|
name | Field name matching the <body_1>, <header_1>, <button_1> placeholders used in example_payload, so a filled-in field maps straight onto the matching slot |
label | Human-readable label. Always in English — it describes the API contract, not the message language |
type | text for placeholder values, url for header_media_url |
required | Whether a value must be supplied |
help | Sample value, when the template provides one. Omitted otherwise |
Fields are returned in the order the parts appear in the rendered message: header → header media → body → buttons.
Error responses
Errors return a 4xx status and a JSON body in the shape:
{
"error": true,
"error_message": "Payload is invalid: template_uuid is not valid"
}
| Status | error_message | When |
|---|---|---|
400 | Payload is invalid: template_uuid is not valid | Template does not exist, or does not belong to your account |
400 | Payload is invalid: fields_format only supports "flat" | fields_format was set to anything other than flat |
422 | Validation error | template_uuid does not match the expected TMP… format |
This endpoint returns templates in any status, including PENDING and REJECTED, so you can inspect them. Only APPROVED templates can actually be sent.