import requests
url = "https://pombo.digital/api/integrations/v1/templates"
payload = {
"name": "cobranca_vencida",
"body": "Prezado(a) {{1}}, consta débito de R$ {{2}} com vencimento em {{3}}.",
"template_type": "TEXT",
"variables": {
"{{1}}": {
"variable_name": "nome",
"variable_example": "Maria Silva"
},
"{{2}}": {
"variable_name": "valor",
"variable_example": "1.250,00"
},
"{{3}}": {
"variable_name": "vencimento",
"variable_example": "10/09/2026"
}
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"template_id": "PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
"organization_id": "PBD_ORG_01J8A1B2C3D4E5F6G7H8J9K0LM",
"name": "cobranca_vencida",
"channel": "whatsapp",
"body": "Prezado(a) {{1}}, consta débito de R$ {{2}} com vencimento em {{3}}.",
"template_type": "TEXT",
"language_code": "pt_BR",
"template_status": "PENDING",
"template_origin": "ORG",
"declined_reason": null,
"rejected_reason": null,
"updated_at": "2026-08-27T14:03:11.482Z",
"variables": {
"{{1}}": {
"variable_name": "nome",
"variable_example": "Maria Silva"
}
},
"created_at": "2026-08-27T14:03:11.482Z"
}Criar um modelo
Cria um modelo de mensagem, o texto reutilizável que toda notificação usa.
import requests
url = "https://pombo.digital/api/integrations/v1/templates"
payload = {
"name": "cobranca_vencida",
"body": "Prezado(a) {{1}}, consta débito de R$ {{2}} com vencimento em {{3}}.",
"template_type": "TEXT",
"variables": {
"{{1}}": {
"variable_name": "nome",
"variable_example": "Maria Silva"
},
"{{2}}": {
"variable_name": "valor",
"variable_example": "1.250,00"
},
"{{3}}": {
"variable_name": "vencimento",
"variable_example": "10/09/2026"
}
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"template_id": "PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
"organization_id": "PBD_ORG_01J8A1B2C3D4E5F6G7H8J9K0LM",
"name": "cobranca_vencida",
"channel": "whatsapp",
"body": "Prezado(a) {{1}}, consta débito de R$ {{2}} com vencimento em {{3}}.",
"template_type": "TEXT",
"language_code": "pt_BR",
"template_status": "PENDING",
"template_origin": "ORG",
"declined_reason": null,
"rejected_reason": null,
"updated_at": "2026-08-27T14:03:11.482Z",
"variables": {
"{{1}}": {
"variable_name": "nome",
"variable_example": "Maria Silva"
}
},
"created_at": "2026-08-27T14:03:11.482Z"
}Authorizations
Sua credencial de API no cabeçalho Authorization: Bearer sk_live_.... Crie e revogue credenciais no painel, em Conta → API Keys. O segredo aparece uma única vez, na criação.
A credencial identifica a organização: organization_id não é aceito em nenhuma requisição.
Body
language_code e template_category_type são definidos pelo servidor. Enviá-los é recusado com 400 TEMPLATE_CONSTANT_FORBIDDEN.
Nome do modelo, único na organização.
Um nome repetido não é recusado: o Pombo acrescenta -2, -3 e cria um modelo novo. Ele aparece na listagem, mas encontrá-lo não o desfaz, porque a API não apaga modelos e em whatsapp ele já foi para aprovação da Meta.
Idempotency-Key não vale nesta chamada. Liste antes em vez de repetir às cegas.
1"cobranca_vencida"
O corpo da mensagem, com uma regra de conteúdo por canal.
Em whatsapp: texto puro, a Meta não aceita marcação, e o tamanho é validado na criação contando o exemplo de cada variável.
Em email: aceita HTML, e o tamanho não é limitado.
Nos dois canais, os marcadores são numerados e sequenciais: {{1}}, {{2}}, {{3}}. Um marcador com texto, como {{nome}}, é recusado com 400 Formato inválido (placeholder com texto).
"Prezado(a) {{1}}, consta débito de R$ {{2}} com vencimento em {{3}}."
O tipo de cabeçalho, e é ele que decide se a mensagem leva um arquivo.
TEXT: só texto. É o caso normal e o único aceito em envio em massa.DOCUMENT: a mensagem leva um documento.IMAGE: a mensagem leva uma imagem.
Obrigatório em whatsapp, que é o canal padrão. Em email o Pombo assume TEXT e só TEXT é aceito: outro valor é recusado com 400 Invalid request: template_type inválido para email. Valor aceito: TEXT.
No envio o par é obrigatório nos dois sentidos: um modelo DOCUMENT ou IMAGE enviado como application/json, sem arquivo, é recusado com 400, e um modelo TEXT enviado como multipart/form-data, com arquivo, também. A Meta aprova o tipo de cabeçalho junto com o modelo, então mudá-lo depois exige uma nova aprovação.
TEXT, DOCUMENT, IMAGE "TEXT"
As variáveis do corpo, uma entrada por marcador. Exemplo:
"variables": {
"{{1}}": { "variable_name": "nome", "variable_example": "Maria Silva" },
"{{2}}": { "variable_name": "valor", "variable_example": "1.250,00" },
"{{3}}": { "variable_name": "vencimento", "variable_example": "10/09/2026" }
}
A chave é o marcador literal, entre chaves duplas: "{{1}}". Não é o índice 1 nem o variable_name. Envie {} se o corpo não tiver marcadores.
As chaves precisam ser consecutivas a partir de {{1}}. Um vão, como {{1}} e {{3}} sem {{2}}, é recusado com 400.
Hide child attributes
Hide child attributes
Hide child attributes
Hide child attributes
Um rótulo seu, para se orientar. Não é a chave.
"nome"
Um valor de exemplo. Em whatsapp a Meta o usa para aprovar o modelo.
Em email a marcação é removida antes de armazenar, então um exemplo com HTML volta sem ele em GET /templates/{id}.
"Maria Silva"
Em minúsculas aqui, diferente de channel no envio, que é em maiúsculas.
Opcional: omitido, vale whatsapp, e com ele vêm a exigência de template_type e a aprovação da Meta. Para e-mail, informe email explicitamente.
whatsapp, email "whatsapp"
Response
Modelo criado. Guarde o template_id: a listagem também o devolve, mas guardar poupa uma chamada.
whatsapp, email O body com cada marcador substituído pelo variable_example da variável, entre colchetes. Montado pelo Pombo, serve para pré-visualizar o texto sem preencher nada.
O tipo de cabeçalho. DOCUMENT e IMAGE são os que levam arquivo; TEXT é só texto.
VIDEO e LOCATION podem aparecer em modelos antigos e não são aceitos ao criar um modelo novo. Um envio com eles não leva arquivo.
DOCUMENT, IMAGE, LOCATION, TEXT, VIDEO pt_BR, en_US, es_ES Só um modelo APPROVED pode ser usado em um envio de WhatsApp. A aprovação é da Meta, não do Pombo.
Um modelo de whatsapp nasce PENDING: consulte GET /templates/{id} até chegar a APPROVED, e crie os seus modelos com antecedência, porque um envio com modelo não aprovado é recusado com 422.
Um modelo de email nasce APPROVED e serve para enviar na mesma hora.
PENDING, APPROVED, REJECTED, PAUSED, DISABLED, DELETED, IN_APPEAL, FLAGGED, FAILED SYSTEM são modelos do catálogo do Pombo; ORG, os da sua organização.
Na listagem, os seus aparecem em qualquer template_status, inclusive PENDING e REJECTED; os do catálogo aparecem só quando já estão APPROVED. Rascunhos e modelos apagados ficam de fora.
SYSTEM, ORG Anotação de quem criou o modelo, sem efeito nenhum: nada no envio a consulta, e enviar sem arquivo não é recusado por causa dela. Não a envie.
Um modelo de whatsapp devolve sempre null, tenha você enviado o campo ou não; só um modelo de email devolve o valor como chegou.
Para um modelo que leve arquivo, o campo que importa é template_type, com DOCUMENT ou IMAGE.
As variáveis do modelo. Estas chaves são as de parameters no envio.
Os marcadores do rodapé do remetente estão deliberadamente ausentes daqui, embora apareçam no body: é o Pombo que os preenche, a partir do sender do envio. Um marcador do body sem entrada em variables é isso, e não um erro.
Hide child attributes
Hide child attributes
Hide child attributes
Hide child attributes
Um rótulo seu, para se orientar. Não é a chave.
"nome"
Um valor de exemplo. Em whatsapp a Meta o usa para aprovar o modelo.
Em email a marcação é removida antes de armazenar, então um exemplo com HTML volta sem ele em GET /templates/{id}.
"Maria Silva"
Campo histórico, sem efeito: não é o assunto que o destinatário recebe. O assunto de um e-mail é montado no momento do envio a partir do nome da organização.
Sempre null em um modelo criado pela API, nos dois canais: o campo não é gravado na criação. Só traz conteúdo em modelos antigos.
Por que a Meta recusou o modelo, quando template_status é REJECTED ou FAILED. null enquanto não houve recusa.
ABUSIVE_CONTENT e SCAM exigem reescrever o texto. INVALID_FORMAT, TAG_CONTENT_MISMATCH e INCORRECT_CATEGORY são erros de forma. REVIEW_TIMEOUT é falha da própria revisão e um modelo novo com o mesmo texto pode passar.
ABUSIVE_CONTENT, SCAM, INVALID_FORMAT, TAG_CONTENT_MISMATCH, INCORRECT_CATEGORY, REVIEW_TIMEOUT, UNKNOWN, null O motivo em texto livre, como o provedor o enviou. Não é estável e não deve ser comparado em código: para ramificar, use declined_reason.
A última alteração do registro. Muda sem você fazer nada: a Meta atualiza o status e a categoria por webhook, e cada atualização mexe neste campo.

