Pular para o conteúdo principal

Templates

Template é uma mensagem aprovada previamente pela Meta. É a única forma de escrever para alguém fora da janela de 24 horas.

Templates pertencem à WABA (conta empresarial), não a um número — todos os números da mesma conta compartilham a mesma biblioteca.

Categorias​

CategoriaPara quêCusto
UTILITYConfirmação, atualização de pedido, lembrete de agendamentobaixo
AUTHENTICATIONCódigo de verificaçãobaixo
MARKETINGPromoção, novidade, reengajamentomais alto

A Meta reclassifica templates que ela julga mal categorizados. Um "utilitário" que na prática vende vira MARKETING, e o custo sobe. allowCategoryChange: true (o padrão) deixa isso acontecer em vez de rejeitar o template.

Ciclo de vida​

A aprovação leva de alguns minutos a algumas horas. O resultado chega pelo webhook message_template_status_update — não fique consultando em loop.

PAUSED acontece quando muitos clientes bloqueiam ou denunciam mensagens daquele template. Ele volta sozinho se a qualidade se recuperar; se piorar, vira DISABLED e não envia mais.

Variáveis e exemplos​

Todo template com variáveis precisa declarar exemplos — sem eles a Meta rejeita. O Joinotify valida isso antes de enviar e devolve 422 com o campo issues apontando o problema, o que evita o ciclo lento de submeter, esperar e ser rejeitado.

Formato posicional​

{
"name": "confirmacao_pedido",
"category": "UTILITY",
"language": "pt_BR",
"components": [
{
"type": "BODY",
"text": "Olá {{1}}, seu pedido {{2}} foi confirmado e chega em {{3}}.",
"example": { "body_text": [["Maria", "1042", "2 dias úteis"]] }
}
]
}

body_text é um array de arrays — a Meta permite mais de um conjunto de exemplos. Um só já basta.

Formato nomeado​

{
"type": "BODY",
"text": "Olá {{nome}}, seu pedido {{numero}} foi confirmado.",
"example": {
"body_text_named_params": [
{ "param_name": "nome", "example": "Maria" },
{ "param_name": "numero", "example": "1042" }
]
}
}

Mais legível quando o template tem muitas variáveis. Escolha um formato e fique nele.

As regras validadas​

RegraPor quê
Toda variável precisa de exemploA Meta usa o exemplo para revisar o template
Exemplo não pode ser vazioVira rejeição automática
Numeração posicional sem lacunas{{1}}, {{3}} sem {{2}} é inválido
Não misturar posicional e nomeadoMesmo template, um formato só
Header de texto: no máximo 1 variávelLimite da Meta
Footer: nenhuma variávelLimite da Meta
Botão de URL com variável exige exemplo de URL completaA Meta valida o domínio

Quando algo falha:

422 Unprocessable Entity
{
"error": {
"message": "Invalid template components.",
"type": "invalid_request",
"issues": [{ "path": "components[0].text", "message": "Variable {{2}} has no example value." }]
}
}

Criando​

curl -X POST https://api.joinotify.com/templates \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "confirmacao_pedido",
"category": "UTILITY",
"language": "pt_BR",
"components": [
{ "type": "HEADER", "format": "TEXT", "text": "Pedido confirmado" },
{
"type": "BODY",
"text": "Olá {{1}}, seu pedido {{2}} foi confirmado.",
"example": { "body_text": [["Maria", "1042"]] }
},
{ "type": "FOOTER", "text": "Joinotify" },
{
"type": "BUTTONS",
"buttons": [
{ "type": "URL", "text": "Acompanhar", "url": "https://exemplo.com/pedidos/{{1}}", "example": ["https://exemplo.com/pedidos/1042"] }
]
}
]
}'

O name aceita só minúsculas, números e underscore. Ele é a chave de envio — escolha pensando em como vai chamá-lo no código, não em como aparece para o cliente.

Listando​

curl 'https://api.joinotify.com/templates?status=APPROVED' \
-H 'Authorization: Bearer sk_live_xxx'

A listagem vem de um espelho local, que é atualizado por webhook e sincronizado sob demanda quando fica mais velho que 15 minutos.

CampoPara quê
staletrue quando o espelho está velho e a sincronização falhou
syncedAtQuando o espelho foi atualizado pela última vez
syncResumo da sincronização, quando uma rodou nesta requisição
syncErrorMotivo da falha — os dados em cache ainda vêm na resposta

refresh=true força a sincronização; refresh=false serve só o cache. Para uma tela de listagem, refresh=false é o certo: é instantâneo e o espelho já se mantém sozinho.

Sincronizando manualmente​

curl -X POST https://api.joinotify.com/templates/sync \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "all": true }'

Só é necessário em dois casos: templates criados direto no Business Manager, ou suspeita de que um webhook se perdeu. Uma sincronização já em andamento devolve 202 e nada é duplicado.

Removendo​

curl -X DELETE 'https://api.joinotify.com/templates/confirmacao_pedido' \
-H 'Authorization: Bearer sk_live_xxx'

A remoção vale para todos os idiomas daquele nome. Mensagens já enviadas continuam nas conversas dos clientes.

O nome fica bloqueado por 30 dias

Depois de apagar, a Meta não deixa criar outro template com o mesmo nome por 30 dias. Se a intenção é corrigir o conteúdo, edite em vez de apagar e recriar.

Enviando​

curl -X POST https://api.joinotify.com/messages \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"type": "template",
"to": "5541987111527",
"name": "confirmacao_pedido",
"language": "pt_BR",
"components": [
{ "type": "header", "parameters": [{ "type": "text", "text": "Pedido confirmado" }] },
{ "type": "body", "parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "1042" }
]},
{ "type": "button", "sub_type": "url", "index": "0",
"parameters": [{ "type": "text", "text": "1042" }] }
]
}'

Note a assimetria: na criação os componentes vão em maiúsculas (BODY), no envio em minúsculas (body). É assim na Meta e nós repassamos sem alterar.

Em botão de URL, o parameter é apenas a parte variável — no exemplo acima, 1042 completa https://exemplo.com/pedidos/{{1}}.

Erros comuns no envio​

Código da MetaO que houve
132000Número de parâmetros não bate com o template
132001Template não existe nesse idioma
132005Parâmetro longo demais
132007Template com formato inválido
132012Formato de parâmetro incorreto
132015Template pausado por qualidade
132016Template desabilitado por qualidade

Todos chegam dentro de error.meta numa resposta 422 meta_error.