New skills covering 10 categories: **Security & Audit**: 007 (STRIDE/PASTA/OWASP), cred-omega (secrets management) **AI Personas**: Karpathy, Hinton, Sutskever, LeCun (4 sub-skills), Altman, Musk, Gates, Jobs, Buffett **Multi-agent Orchestration**: agent-orchestrator, task-intelligence, multi-advisor **Code Analysis**: matematico-tao (Terence Tao-inspired mathematical code analysis) **Social & Messaging**: Instagram Graph API, Telegram Bot, WhatsApp Cloud API, social-orchestrator **Image Generation**: AI Studio (Gemini), Stability AI, ComfyUI Gateway, image-studio router **Brazilian Domain**: 6 auction specialist modules, 2 legal advisors, auctioneers data scraper **Product & Growth**: design, invention, monetization, analytics, growth engine **DevOps & LLM Ops**: Docker/CI-CD/AWS, RAG/embeddings/fine-tuning **Skill Governance**: installer, sentinel auditor, context management Each skill includes: - Standardized YAML frontmatter (name, description, risk, source, tags, tools) - Structured sections (Overview, When to Use, How it Works, Best Practices) - Python scripts and reference documentation where applicable - Cross-platform compatibility (Claude Code, Antigravity, Cursor, Gemini CLI, Codex CLI) Co-authored-by: ProgramadorBrasil <214873561+ProgramadorBrasil@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
Gerenciamento de Templates via API - WhatsApp Cloud API
Guia completo para criar, listar, deletar e gerenciar templates de mensagem programaticamente via WhatsApp Business Management API.
Indice
- Visao Geral
- Categorias de Templates
- Criar Template
- Listar Templates
- Deletar Template
- Templates com Variaveis
- Templates com Midia
- Templates com Botoes
- Enviar Template Message
- Boas Praticas
Visao Geral
Templates sao mensagens pre-aprovadas pela WhatsApp. Sao a unica forma de iniciar conversa com um cliente (fora da janela de 24h).
Limites:
- Ate 6,000 traducoes de templates por conta WABA
- Aprovacao leva de minutos a poucas horas
- Templates nao podem ser editados apos submissao (delete e crie novo)
- Template body: max 1,600 caracteres
Endpoint base: https://graph.facebook.com/v21.0/{waba-id}/message_templates
Categorias de Templates
| Categoria | Uso | Custo |
|---|---|---|
| MARKETING | Promocoes, campanhas, lancamentos | $0.025-$0.1365/msg |
| UTILITY | Confirmacoes de pedido, atualizacoes, tracking | $0.004-$0.0456/msg |
| AUTHENTICATION | OTP, reset de senha, verificacao em 2 etapas | $0.004-$0.0456/msg |
A categoria afeta o custo e as regras de aprovacao. Templates de marketing tem regras mais rigorosas.
Criar Template
Node.js
interface TemplateComponent {
type: 'HEADER' | 'BODY' | 'FOOTER' | 'BUTTONS';
format?: 'TEXT' | 'IMAGE' | 'VIDEO' | 'DOCUMENT';
text?: string;
example?: { header_handle?: string[]; body_text?: string[][] };
buttons?: Array<{
type: 'QUICK_REPLY' | 'URL' | 'PHONE_NUMBER';
text: string;
url?: string;
phone_number?: string;
example?: string[];
}>;
}
async function createTemplate(
name: string,
category: 'MARKETING' | 'UTILITY' | 'AUTHENTICATION',
language: string,
components: TemplateComponent[]
): Promise<any> {
const response = await axios.post(
`${GRAPH_API}/${process.env.WABA_ID}/message_templates`,
{ name, category, language, components },
{ headers: { Authorization: `Bearer ${process.env.WHATSAPP_TOKEN}` } }
);
return response.data;
// { id: "template_id", status: "PENDING", category: "UTILITY" }
}
// Exemplo: Criar template de confirmacao de pedido
await createTemplate(
'order_confirmation_v1',
'UTILITY',
'pt_BR',
[
{
type: 'HEADER',
format: 'TEXT',
text: 'Pedido Confirmado!'
},
{
type: 'BODY',
text: 'Ola {{1}}, seu pedido #{{2}} foi confirmado!\n\nValor: R$ {{3}}\nPrevisao de entrega: {{4}}',
example: {
body_text: [['Joao', '12345', '99,90', '3 dias uteis']]
}
},
{
type: 'FOOTER',
text: 'Obrigado por comprar conosco!'
}
]
);
Python
async def create_template(
name: str,
category: str,
language: str,
components: list[dict]
) -> dict:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{GRAPH_API}/{os.environ['WABA_ID']}/message_templates",
json={
"name": name,
"category": category,
"language": language,
"components": components
},
headers={"Authorization": f"Bearer {os.environ['WHATSAPP_TOKEN']}"}
)
return response.json()
# Exemplo: Criar template de boas-vindas
await create_template(
name="welcome_v1",
category="MARKETING",
language="pt_BR",
components=[
{
"type": "BODY",
"text": "Ola {{1}}, bem-vindo a nossa loja! 🎉\n\nConfira nossas ofertas exclusivas.",
"example": {"body_text": [["Maria"]]}
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Ver Ofertas",
"url": "https://example.com/ofertas"
},
{
"type": "QUICK_REPLY",
"text": "Falar com Vendedor"
}
]
}
]
)
Listar Templates
Node.js
async function listTemplates(status?: string): Promise<any[]> {
const params = new URLSearchParams({ limit: '100' });
if (status) params.append('status', status);
const response = await axios.get(
`${GRAPH_API}/${process.env.WABA_ID}/message_templates?${params}`,
{ headers: { Authorization: `Bearer ${process.env.WHATSAPP_TOKEN}` } }
);
return response.data.data;
}
// Listar apenas templates aprovados
const approved = await listTemplates('APPROVED');
// Listar todos
const all = await listTemplates();
Python
async def list_templates(status: str | None = None) -> list[dict]:
params = {"limit": 100}
if status:
params["status"] = status
async with httpx.AsyncClient() as client:
response = await client.get(
f"{GRAPH_API}/{os.environ['WABA_ID']}/message_templates",
params=params,
headers={"Authorization": f"Bearer {os.environ['WHATSAPP_TOKEN']}"}
)
return response.json()["data"]
Status de Template
| Status | Significado |
|---|---|
| APPROVED | Aprovado e pronto para uso |
| PENDING | Em revisao pela WhatsApp |
| REJECTED | Rejeitado (ver motivo na response) |
| PAUSED | Pausado por baixa qualidade |
| DISABLED | Desabilitado |
Deletar Template
Node.js
async function deleteTemplate(templateName: string): Promise<void> {
await axios.delete(
`${GRAPH_API}/${process.env.WABA_ID}/message_templates`,
{
data: { name: templateName },
headers: { Authorization: `Bearer ${process.env.WHATSAPP_TOKEN}` }
}
);
}
await deleteTemplate('old_template_v1');
Python
async def delete_template(template_name: str) -> None:
async with httpx.AsyncClient() as client:
await client.request(
"DELETE",
f"{GRAPH_API}/{os.environ['WABA_ID']}/message_templates",
json={"name": template_name},
headers={"Authorization": f"Bearer {os.environ['WHATSAPP_TOKEN']}"}
)
Nota: Deletar um template remove TODAS as traducoes associadas.
Templates com Variaveis
Variaveis sao representadas por {{N}} (1-indexed) no texto do template.
Regras
- Variaveis devem ser sequenciais:
{{1}},{{2}},{{3}} - Ao criar, fornecer
examplecom valores de exemplo - Ao enviar, fornecer
parameterscom valores reais - Nao pule numeros:
{{1}},{{3}}sem{{2}}e invalido
Exemplo Completo
Criar:
{
"type": "BODY",
"text": "Ola {{1}}, seu pedido #{{2}} sera entregue em {{3}}.",
"example": { "body_text": [["Joao", "12345", "2 dias"]] }
}
Enviar:
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "67890" },
{ "type": "text", "text": "3 dias uteis" }
]
}
Templates com Midia
Header com Imagem
Criar:
{
"type": "HEADER",
"format": "IMAGE",
"example": {
"header_handle": ["4::aW1hZ2UvanBlZw==:ARb..."]
}
}
Para obter o header_handle, faca upload da imagem de exemplo primeiro:
POST /{app-id}/uploads?file_type=image/jpeg&file_length=12345
Enviar:
{
"type": "header",
"parameters": [
{
"type": "image",
"image": { "link": "https://example.com/image.jpg" }
}
]
}
Header com Documento
Criar:
{
"type": "HEADER",
"format": "DOCUMENT",
"example": {
"header_handle": ["4::YXBwbGljYXRpb24vcGRm:ARb..."]
}
}
Enviar:
{
"type": "header",
"parameters": [
{
"type": "document",
"document": {
"link": "https://example.com/invoice.pdf",
"filename": "Nota_Fiscal_12345.pdf"
}
}
]
}
Templates com Botoes
Quick Reply (ate 3 botoes)
{
"type": "BUTTONS",
"buttons": [
{ "type": "QUICK_REPLY", "text": "Sim, confirmo" },
{ "type": "QUICK_REPLY", "text": "Nao, cancelar" },
{ "type": "QUICK_REPLY", "text": "Falar com atendente" }
]
}
URL Button
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Rastrear Pedido",
"url": "https://example.com/tracking/{{1}}",
"example": ["12345"]
}
]
}
Phone Number Button
{
"type": "BUTTONS",
"buttons": [
{
"type": "PHONE_NUMBER",
"text": "Ligar para Suporte",
"phone_number": "+5511999999999"
}
]
}
Enviar Template com Botao URL Dinamico
await sendMessage({
messaging_product: 'whatsapp',
to: '5511999999999',
type: 'template',
template: {
name: 'order_tracking_v1',
language: { code: 'pt_BR' },
components: [
{
type: 'body',
parameters: [
{ type: 'text', text: 'Maria' },
{ type: 'text', text: '67890' }
]
},
{
type: 'button',
sub_type: 'url',
index: 0,
parameters: [
{ type: 'text', text: '67890' } // substitui {{1}} na URL
]
}
]
}
});
Enviar Template Message
Exemplo Completo - Node.js
async function sendTemplate(
to: string,
templateName: string,
language: string,
components?: Array<{
type: string;
parameters?: Array<{ type: string; text?: string; image?: any; document?: any }>;
sub_type?: string;
index?: number;
}>
): Promise<any> {
const payload: any = {
messaging_product: 'whatsapp',
to,
type: 'template',
template: {
name: templateName,
language: { code: language }
}
};
if (components) {
payload.template.components = components;
}
return sendWithRetry(payload);
}
// Uso simples (sem variaveis)
await sendTemplate('5511999999999', 'hello_world', 'pt_BR');
// Com variaveis no body
await sendTemplate('5511999999999', 'order_confirmation_v1', 'pt_BR', [
{
type: 'body',
parameters: [
{ type: 'text', text: 'Joao' },
{ type: 'text', text: '12345' },
{ type: 'text', text: '99,90' },
{ type: 'text', text: '3 dias uteis' }
]
}
]);
Exemplo Completo - Python
async def send_template(
to: str,
template_name: str,
language: str,
components: list[dict] | None = None
) -> dict:
payload = {
"messaging_product": "whatsapp",
"to": to,
"type": "template",
"template": {
"name": template_name,
"language": {"code": language}
}
}
if components:
payload["template"]["components"] = components
return await send_with_retry(payload)
# Uso simples
await send_template("5511999999999", "hello_world", "pt_BR")
# Com variaveis
await send_template("5511999999999", "order_confirmation_v1", "pt_BR", [
{
"type": "body",
"parameters": [
{"type": "text", "text": "Maria"},
{"type": "text", "text": "67890"},
{"type": "text", "text": "149,90"},
{"type": "text", "text": "5 dias uteis"}
]
}
])
Boas Praticas
Nomenclatura
Use um padrao consistente para nomes de templates:
{finalidade}_{descricao}_v{versao}
Exemplos:
order_confirmation_v1welcome_new_customer_v2payment_reminder_v1nps_survey_v3
Versionamento
Como templates nao podem ser editados:
- Crie nova versao:
template_name_v2 - Teste a nova versao
- Quando aprovada, migre o codigo para usar a v2
- Delete a v1 quando nao mais necessaria
Dicas de Aprovacao
- Evite linguagem excessivamente promocional no corpo
- Inclua exemplos claros e reais no
example - Nao use URLs encurtadas (bit.ly, etc.)
- Nao inclua conteudo que possa ser interpretado como spam
- Utility templates tem aprovacao mais rapida que marketing
- Use variaveis para personalizar (nome do cliente, numero do pedido)
Monitoramento
// Verificar status de templates periodicamente
async function monitorTemplates(): Promise<void> {
const templates = await listTemplates();
for (const template of templates) {
if (template.status === 'REJECTED') {
console.warn(`Template rejeitado: ${template.name}`);
console.warn(`Motivo: ${template.rejected_reason}`);
}
if (template.status === 'PAUSED') {
console.warn(`Template pausado por qualidade: ${template.name}`);
}
}
}