Skip to main content
Version: Latest

WhatsApp Templates

Approved WhatsApp templates allow messages outside the 24-hour customer-service window. Within an active window, you can also send ordinary supported messages. A template must match its approved structure, channel, language and current provider availability.

Template button limits​

For standard WhatsApp templates, Meta allows up to 10 buttons in total, subject to these per-type limits and the selected template category/type:

Button typeMaximum per templateLabel limit
Quick Reply (QUICK_REPLY)1025 characters
Website URL (URL)225 characters
Phone number (PHONE_NUMBER)125 characters
Copy code/coupon (COPY_CODE)1Defined by the template type

The per-type limits do not add extra capacity beyond the ten-button total. Traditional URL/phone CTAs therefore allow at most two URL buttons and one phone-number button. For example, two URLs, one phone-number button and seven quick replies reach the total of ten.

When mixing quick replies with other button types, keep them in two contiguous groups: quick replies and other buttons. [URL, PHONE_NUMBER, QUICK_REPLY, QUICK_REPLY] is a valid grouping; [QUICK_REPLY, URL, QUICK_REPLY] is invalid. Category/type restrictions still apply; authentication/OTP, Flow, voice call, carousel and other specialized templates follow their own rules and provider eligibility.

Templates retain these rules when sent inside the 24-hour window. They do not become ordinary session messages; see the separate session-message limits. The public send API supplies parameters for an already approved template, rather than creating or redefining its buttons.

When a template has more than three buttons, WhatsApp displays two buttons and puts the remaining ones behind See all options. Meta also documents desktop presentation restrictions for templates with four or more buttons, or quick replies mixed with other button types; verify the target client.

See Meta's template component reference for the provider rules.

Creating a template in the dashboard​

In the Hub Chatt2.me dashboard, open Channels → your WhatsApp channel → Templates, choose Create template, complete its content and submit for approval. Creation and management happen in the dashboard; the public integration API lists synchronized templates and sends them.

FieldDescription
NameUnique snake_case identifier, such as confirm_order
CategoryMARKETING, UTILITY, or AUTHENTICATION
LanguageFor example pt_BR or en_US
VariablesNumbered {{1}}, {{2}} or named {{customer_name}}, depending on the selected format
BodyApproved message text and example values for its variables
HeaderOptional text, image, video, document or location
Media modeSTATIC reuses saved media; DYNAMIC supplies media for each send
FooterFixed approved text
ButtonsDepending on template type/category: Quick Reply, static/dynamic URL, phone number, Copy Code, voice call or Flow

The dashboard also offers specialized template types such as Flow, order details/status and authentication codes. The available options depend on the selected category and provider eligibility. A fixed button label, URL, phone number or Flow selection belongs to the approved template; send parameters only supply its variable values or action data. Changing a send payload does not redefine the approved button.

Listing synchronized templates​

GET /v1/communication/channel/whatsapp/templates/{applicationChannelId}

Use x-api-key. The channel identifier accepts its UUID or configured WhatsApp number. The result is the locally synchronized catalog, rather than a live provider query.

Filter by status, category, languageCode and search. offset is the number of templates to skip (default 0); limit defaults to and cannot exceed 100. Increase offset by limit for the next page.

curl "https://app.chatt2.me/v1/communication/channel/whatsapp/templates/9e85226d-a104-4c85-a21a-5c3cbf7daa8f?status=APPROVED&languageCode=pt_BR&search=order&offset=0&limit=100" \
-H "x-api-key: YOUR_API_KEY"
{
"applicationChannelId": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"phoneNumber": "551130001234",
"templates": [
{
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"elementName": "confirm_order",
"languageCode": "pt_BR",
"status": "APPROVED",
"category": "UTILITY"
}
],
"pagination": {
"offset": 0,
"limit": 100,
"total": 1
}
}

Use templates[].id as content.template.id. A missing template or one belonging to another channel is unavailable to the send request.

Sandbox catalog​

For a WhatsApp sandbox channel, listing returns a localized read-only compatibility catalog. Its phoneNumber is null; sandboxFamily identifies text_variable, quick_reply or image. Extra metadata such as containerMeta and meta can be JSON-encoded strings. Use the returned ID and supported components from Sandbox; the advanced examples below require actual compatible synchronized templates.

Sending templates​

POST these complete bodies to /v1/communication/whatsapp/message with x-api-key and Content-Type: application/json. Replace the channel UUID, recipient and template UUID with your own. from also accepts the configured business phone number. Each example assumes an approved template matching its components; these illustrative IDs do not identify templates in your account.

No send-time parameters​

Use this for a template with fixed content and no required dynamic media or variables:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514"
}
}
}

Positional body variables​

For body text such as Hello {{1}}, your order {{2}} is confirmed, every text parameter requires both variable and text. Numbered variables use string keys and positional order:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "Maria"
},
{
"type": "text",
"variable": "2",
"text": "order-12345"
}
]
}
]
}
}
}

Named body variables​

For a template created with the NAMED format and body Hello {{customer_name}}, your order {{order_number}} is confirmed:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "customer_name",
"text": "Maria"
},
{
"type": "text",
"variable": "order_number",
"text": "order-12345"
}
]
}
]
}
}
}

Use the exact approved variable names. Hub converts variable to the provider's parameter_name for NAMED templates; for POSITIONAL templates it removes the helper key and preserves parameter order. Do not send parameter_name directly to the Hub API. Omitting variable or text from a text parameter fails validation.

Variable text header​

For a header Order {{1}} and body Hello {{1}}:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "header",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "order-12345"
}
]
},
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "Maria"
}
]
}
]
}
}
}

Media headers: STATIC and DYNAMIC​

A STATIC image/video/document template uses the original media URL saved with the template. Omit the header component; Hub inserts its saved media. If you send another header, Hub replaces it with that saved header. Body and button components remain available:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "Maria"
}
]
}
]
}
}
}

This example assumes a STATIC media template whose body has one positional variable. A fixed-body STATIC template can be sent with only its ID.

A DYNAMIC image/video/document template requires a nonempty media link in the first parameter of the header component. Media id is accepted structurally, but an ID-only dynamic header does not satisfy that requirement and returns TEMPLATE_HEADER_MEDIA_LINK_REQUIRED. Use accessible URLs and the type matching the approved header. Do not treat a media ID as a universal replacement for a link.

Dynamic image header and body​

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "header",
"parameters": [
{
"type": "image",
"image": {
"link": "https://example.com/banner.jpg"
}
}
]
},
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "Maria"
}
]
}
]
}
}
}

Dynamic video header​

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "header",
"parameters": [
{
"type": "video",
"video": {
"link": "https://example.com/order-demo.mp4"
}
}
]
}
]
}
}
}

Dynamic document header​

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "header",
"parameters": [
{
"type": "document",
"document": {
"link": "https://example.com/contract.pdf",
"filename": "Contract.pdf"
}
}
]
}
]
}
}
}

Location header​

Location parameters use coordinates and optional name/address:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "header",
"parameters": [
{
"type": "location",
"location": {
"latitude": -23.5505,
"longitude": -46.6333,
"name": "Our store",
"address": "Av. Paulista, 1000, São Paulo, SP"
}
}
]
}
]
}
}
}

Button parameters​

Use type: "button", the approved sub_type, and a numeric zero-based index matching the button's position. Include any required body/header components in the same request. Fixed URL and phone-number buttons do not need a dynamic URL value.

Dynamic URL​

For an approved URL https://example.com/orders/{{1}}, supply only the variable part, order-12345:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "button",
"sub_type": "url",
"index": 0,
"parameters": [
{
"type": "text",
"variable": "1",
"text": "order-12345"
}
]
}
]
}
}
}

Quick Reply callback payload​

For an approved Quick Reply button at index 0:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "button",
"sub_type": "quick_reply",
"index": 0,
"parameters": [
{
"type": "payload",
"payload": "CONFIRM_ORDER_12345"
}
]
}
]
}
}
}

payload is nonempty and limited to 1,024 characters. The displayed label is defined by the approved template. Use the normalized interactive webhook fields to handle the returned identifier.

Copy Code​

For an approved Copy Code button at index 0:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "button",
"sub_type": "copy_code",
"index": 0,
"parameters": [
{
"type": "coupon_code",
"coupon_code": "SAVE10"
}
]
}
]
}
}
}

Specialized action buttons​

The Hub accepts type: "action" with sub_type: "order_details" or "mpm". The following shapes match the dashboard's send examples and DTO forwarding. The Hub does not validate every nested provider action field or prove provider eligibility from DTO acceptance. Use an approved compatible template, valid payment/catalog data and a connection supporting the feature, and test delivery before depending on it.

For an order-details action:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "button",
"sub_type": "order_details",
"index": 0,
"parameters": [
{
"type": "action",
"action": {
"order_details": {
"currency": "BRL",
"order": {
"items": [
{
"retailer_id": "item1",
"name": "Product",
"quantity": 1,
"amount": {
"offset": 100,
"value": 9999
}
}
],
"subtotal": {
"offset": 100,
"value": 9999
},
"status": "pending"
},
"total_amount": {
"offset": 100,
"value": 9999
},
"reference_id": "order-123",
"type": "digital-goods",
"payment_type": "br",
"payment_settings": [
{
"type": "payment_link",
"payment_link": {
"uri": "https://payment.example.com/pay/order-123"
}
}
]
}
}
}
]
}
]
}
}
}

For a multiproduct action, replace retailer IDs with products in the catalog associated with the template/channel:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "button",
"sub_type": "mpm",
"index": 0,
"parameters": [
{
"type": "action",
"action": {
"thumbnail_product_retailer_id": "SKU-001",
"sections": [
{
"title": "Items",
"product_items": [
{
"product_retailer_id": "SKU-001"
},
{
"product_retailer_id": "SKU-002"
}
]
}
]
}
}
]
}
]
}
}
}

Flow templates​

Use an approved synchronized template containing a Flow button and a compatible WhatsApp connection. The Flow ID and its approved behavior belong to the template metadata; a send does not create or publish a Flow.

Automatic Flow button​

For a Flow template with fixed text and no required dynamic header, the ID-only request lets Hub build the Flow action from synchronized metadata:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514"
}
}
}

Hub uses the first Flow button's actual position in the template. When metadata specifies a navigation screen, the automatic action includes it. Required template variables and dynamic media must still be supplied.

Automatic Flow with token and body variables​

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"flow_token": "order-12345-flow-session",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "Maria"
}
]
}
]
}
}
}

content.template.flow_token is optional, nonempty when supplied, and limited to 256 characters. Hub merges the automatic Flow button with the supplied header/body and other button components. STATIC header insertion and DYNAMIC header validation still apply.

Explicit Flow action with header and body​

Use an explicit Flow button to supply token and action data. This example assumes an approved Flow button at index 1, a DYNAMIC image header, one positional body variable and a published screen named ORDER_DETAILS:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "2bb28b3a-80c6-4a63-a6f3-48701044e514",
"components": [
{
"type": "header",
"parameters": [
{
"type": "image",
"image": {
"link": "https://example.com/order-banner.jpg"
}
}
]
},
{
"type": "body",
"parameters": [
{
"type": "text",
"variable": "1",
"text": "Maria"
}
]
},
{
"type": "button",
"sub_type": "flow",
"index": 1,
"parameters": [
{
"type": "action",
"action": {
"flow_token": "order-12345-flow-session",
"flow_action_data": {
"screen": "ORDER_DETAILS",
"data": {
"order_reference": "order-12345"
}
}
}
}
]
}
]
}
}
}

An explicit sub_type: "flow" component replaces automatic Flow composition; its parameters remain alongside the supplied header/body. Put the token in the explicit action when using this path. flow_action_data accepts optional screen (nonempty, at most 256 characters) and data (an object); the token has the same 256-character limit. Use only screen/data values supported by your published Flow. Hub validates the input shape and forwards the data; external Flow behavior is not certified by that validation.

ORDER_STATUS template​

Combine a template of the ORDER_STATUS type with orderStatus. Hub creates the order-status component automatically:

{
"from": "9e85226d-a104-4c85-a21a-5c3cbf7daa8f",
"to": "5511993986082",
"content": {
"template": {
"id": "b50baa16-555b-4cfc-8fa8-29392a6e43d1"
},
"orderStatus": {
"referenceId": "order-987654344",
"status": "shipped"
}
}
}

Do not send custom components in this combination. orderStatus.bodyText is not supported and is discarded; it cannot customize tracking text. See Payment Request & Order Status for compatible connections, window requirements and payment confirmation.

Accepted component and parameter types​

ComponentPurpose
headerText, media or location matching the approved header
bodyApproved text variables
buttonURL, Quick Reply, Copy Code, Flow or compatible specialized action

Text parameters require variable and text. Media/location use image, video, document or location; Copy Code uses coupon_code; Quick Reply uses payload; Flow and specialized buttons use action. The public API does not accept currency or date_time as template parameter types. Payment currency belongs inside the supported order/payment structure, not in a template text parameter.

Categories and billing​

CategoryTypical use
MARKETINGPromotions, offers and campaigns
UTILITYOrder confirmations and transactional notifications
AUTHENTICATIONOTP and account verification

Hub accounts for template outbound usage per send, using the synchronized category: MARKETING, UTILITY or AUTHENTICATION. It does not model MARKETING usage here as one charge per conversation started. Applicable prices and commercial terms are those of your Hub account; confirm them in the dashboard or with support. This guide does not quote or guarantee current external provider pricing. See Application Wallet History for recorded debits.

Next steps​