Files
antigravity-skills-reference/skills/whatsapp-cloud-api/references/template-management.md
ProgramadorBrasil 61ec71c5c7 feat: add 52 specialized AI agent skills (#217)
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>
2026-03-07 10:04:07 +01:00

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

  1. Visao Geral
  2. Categorias de Templates
  3. Criar Template
  4. Listar Templates
  5. Deletar Template
  6. Templates com Variaveis
  7. Templates com Midia
  8. Templates com Botoes
  9. Enviar Template Message
  10. 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 example com valores de exemplo
  • Ao enviar, fornecer parameters com 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_v1
  • welcome_new_customer_v2
  • payment_reminder_v1
  • nps_survey_v3

Versionamento

Como templates nao podem ser editados:

  1. Crie nova versao: template_name_v2
  2. Teste a nova versao
  3. Quando aprovada, migre o codigo para usar a v2
  4. 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}`);
    }
  }
}