WABA Templates
Templates aprovados pela Meta para envio de mensagens proativas (marketing, utility, authentication). Sem template, você só pode mandar mensagem dentro da janela de 24h após uma inbound do usuário.
Base path: /v1/templates
Apenas WABA. Baileys não tem conceito de template.
Lifecycle
POST /v1/templates
│
▼
DRAFT ────► (gateway envia pra Meta sync)
│
▼
PENDING ──► (Meta revisa, ~minutos a ~24h)
│
├──► APPROVED ──► usável em POST /v1/messages type=template
├──► REJECTED ──► verifique rejectionReason
└──► PAUSED ──► (Meta pausou por baixo engagement)
Após APPROVED, você pode editar (volta pra PENDING).
Componentes suportados
Cada template é composto por componentes (HEADER, BODY, FOOTER, BUTTONS, CAROUSEL, LIMITED_TIME_OFFER). O schema completo:
HEADER
{ "type": "HEADER", "format": "TEXT", "text": "Olá {{1}}", "example": { "header_text": ["João"] } }
Formats: TEXT, IMAGE, VIDEO, DOCUMENT, LOCATION.
Para mídia, use example com header_handle (de POST /v1/media/upload/resumable).
BODY
{ "type": "BODY", "text": "Sua consulta {{1}} foi confirmada para {{2}}", "example": { "body_text": [["Cardiologia", "20/05 às 14h"]] } }
Body é obrigatório. Suporta variáveis {{1}}, {{2}}, ... que viram variables em POST /v1/messages.
FOOTER
{ "type": "FOOTER", "text": "Acme Saúde" }
Máx 60 chars, sem variáveis.
BUTTONS
Lista de até 10 botões. Tipos suportados:
| Tipo | Descrição |
|---|---|
QUICK_REPLY | Botão de resposta rápida (texto curto) |
URL | Abre URL (suporta variáveis em URL: https://acme.com/{{1}}) |
PHONE_NUMBER | Disca número |
COPY_CODE | Copia código (cupom, PIX, etc) |
OTP | Authentication template (COPY_CODE/ONE_TAP/ZERO_TAP) |
CATALOG | Abre catálogo da WABA |
MPM | Multi-Product Message |
SPM | Single-Product Message |
FLOW | Abre WhatsApp Flow |
VOICE_CALL | Inicia voice call WABA |
Exemplo URL com variável:
{
"type": "BUTTONS",
"buttons": [
{ "type": "URL", "text": "Ver pedido", "url": "https://acme.com/pedido/{{1}}", "example": ["12345"] }
]
}
CAROUSEL
Marketing template com até 10 cards (cada card = mini-template com HEADER mídia + BODY + BUTTONS).
{
"type": "CAROUSEL",
"cards": [
{
"components": [
{ "type": "HEADER", "format": "IMAGE", "example": { "header_handle": ["h:..."] } },
{ "type": "BODY", "text": "Produto A — R$ {{1}}", "example": { "body_text": [["99,90"]] } },
{ "type": "BUTTONS", "buttons": [{ "type": "URL", "text": "Comprar", "url": "https://acme.com/a" }] }
]
}
]
}
LIMITED_TIME_OFFER
Template com countdown:
{
"type": "LIMITED_TIME_OFFER",
"limited_time_offer": { "text": "Oferta termina em", "has_expiration": true }
}
POST /v1/templates
Cria template e submete à Meta de forma síncrona (a request espera a chamada Graph API).
Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
phoneId | UUID | sim | Phone WABA dono |
name | string | sim | 1-120, [a-z0-9_]+ (lowercase + underscore) |
language | string | sim | pt_BR, en_US, etc. |
category | enum | sim | MARKETING, UTILITY, AUTHENTICATION |
components | array | sim | 1+ componentes |
allowCategoryChange | boolean | não | Default true. Meta pode reclassificar |
messageSendTtlSeconds | int | não | 30-43200 (12h). Auto-fail se não entregue |
curl -X POST https://wpp.ogmma.com.br/v1/templates \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"phoneId": "5f7b2e1c-...",
"name": "boas_vindas_v2",
"language": "pt_BR",
"category": "UTILITY",
"components": [
{ "type": "BODY", "text": "Olá {{1}}, bem-vindo!", "example": { "body_text": [["João"]] } },
{ "type": "FOOTER", "text": "Acme Saúde" },
{
"type": "BUTTONS",
"buttons": [{ "type": "QUICK_REPLY", "text": "Saiba mais" }]
}
]
}'
Response 201
{
"id": "tpl-1234-...",
"accountId": "...",
"phoneId": "5f7b2e1c-...",
"name": "boas_vindas_v2",
"language": "pt_BR",
"category": "UTILITY",
"status": "PENDING",
"components": [ ... ],
"metaTemplateId": "987654321",
"createdAt": "2026-05-21T14:00:00.000Z"
}
Errors
| HTTP | Code | Quando |
|---|---|---|
| 400 | VALIDATION_ERROR | Schema inválido |
| 400 | WABA_CREDS_INCOMPLETE | Phone sem wabaPhoneNumberId/accessToken |
| 404 | PHONE_NOT_FOUND | Phone não é WABA ou não pertence à account |
GET /v1/templates
Lista templates da account.
curl https://wpp.ogmma.com.br/v1/templates \
-H "X-API-Key: ak_live_..."
Response 200 — array.
GET /v1/templates/:id
Detalhe.
curl https://wpp.ogmma.com.br/v1/templates/tpl-1234-... \
-H "X-API-Key: ak_live_..."
Errors
| HTTP | Code | Quando |
|---|---|---|
| 404 | TEMPLATE_NOT_FOUND | — |
PATCH /v1/templates/:id
Edita um template já sincronizado com Meta. Mantém name/language; permite mudar components, category, messageSendTtlSeconds. Após edit, Meta re-revisa (status volta para PENDING).
Body (todos opcionais)
| Campo | Tipo | Obrigatório se quiser editar |
|---|---|---|
components | array | — |
category | enum | — |
messageSendTtlSeconds | int | — |
curl -X PATCH https://wpp.ogmma.com.br/v1/templates/tpl-1234-... \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"components": [
{ "type": "BODY", "text": "Versão atualizada {{1}}", "example": { "body_text": [["X"]] } }
]
}'
Errors
| HTTP | Code | Quando |
|---|---|---|
| 409 | TEMPLATE_NOT_SUBMITTED | Template ainda não tem metaTemplateId |
DELETE /v1/templates/:id
Deleta na Meta + remove local. Se Meta falhar (ex.: template já em uso), remove localmente mesmo assim (log de warn).
curl -X DELETE https://wpp.ogmma.com.br/v1/templates/tpl-1234-... \
-H "X-API-Key: ak_live_..."
Response 204
POST /v1/templates/sync
Re-importa todos os templates de uma WABA direto da Meta (útil quando o time criou templates pelo Business Manager).
Body
| Campo | Tipo | Obrigatório |
|---|---|---|
phoneId | UUID | sim |
curl -X POST https://wpp.ogmma.com.br/v1/templates/sync \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "phoneId": "5f7b2e1c-..." }'
Response 200 — objeto com counts de criados/atualizados.
Notificação de aprovação
Quando a Meta aprova/rejeita um template, o webhook template:status é publicado (assumindo que você subscreve esse evento). O DB local é atualizado automaticamente pelo handler meta-webhook. Você pode também fazer polling em GET /v1/templates/:id.
Boas práticas
- Nomes únicos por phone+language:
boas_vindas_v2+pt_BRé único — versione o nome se mudar muito o body. - Categorias corretas:
MARKETINGé cobrado mais caro queUTILITY. Meta auto-reclassifica se identificar abuse. - Examples obrigatórios: Meta exige
exampleem componentes com variáveis para entender o uso real. - Header media via handle: para template com IMAGE/VIDEO header, não use
url— suba viaPOST /v1/media/upload/resumablee use ohandlenoexample.header_handle.