APIs & Integrações28 de fev. de 2026· 15 min leitura

Template WhatsApp Rejeitado ou Pausado: Como Entender e Corrigir

Entenda o sistema de quality rating do Meta (green/yellow/red), por que templates vão de APPROVED para PAUSED ou DISABLED, como monitorar via API e Business Manager, apelar rejeições e criar um bot de alertas para Slack/Discord.

Você criou um template, ele foi aprovado, tudo funcionando — e de repente para. Status mudou para PAUSED sem aviso claro. Ou pior: você submeteu um template novo e levou REJECTED com uma razão genérica que não explica nada.

Esse ciclo frustrante é o dia a dia de quem trabalha com WhatsApp Business API em produção. Este post explica como o sistema de qualidade do Meta realmente funciona, o que causa cada transição de status, como monitorar programaticamente e como reagir a cada cenário.

O Sistema de Quality Rating do Meta

Todo número de telefone Business na WhatsApp API tem um Quality Rating — uma nota de qualidade baseada no comportamento dos usuários que recebem suas mensagens nos últimos 7 dias.

O rating tem três níveis:

  • Green (Alta qualidade) — Taxa de bloqueios e denúncias dentro do limite. Tudo operando normalmente.
  • Yellow (Média qualidade) — Taxa de bloqueios elevada. Aviso de que mudanças são necessárias antes que a situação piore.
  • Red (Baixa qualidade) — Taxa de bloqueios crítica. Templates pausados automaticamente, capacidade de envio reduzida.

O Que Move o Rating

O Meta não divulga os thresholds exatos, mas os sinais que impactam negativamente são conhecidos:

  1. Usuários bloqueando seu número — A ação mais pesada. Quando alguém bloqueia, é um sinal forte de mensagem indesejada.
  2. Usuários denunciando spamReport Spam dentro do WhatsApp. Penalidade imediata.
  3. Alta taxa de leitura sem resposta — Indicador de conteúdo não relevante.
  4. Volume súbito de envios — Disparar para um grande volume sem histórico prévio levanta flags.

O rating é calculado por número de telefone, não por conta WABA. Se você tem múltiplos números na mesma conta, cada um tem seu próprio rating e afeta seus próprios templates.

Os Status de um Template e Como Transitam

Um template pode estar em qualquer um destes status:

| Status | Descrição | |--------|-----------| | PENDING | Aguardando revisão do Meta | | APPROVED | Aprovado, pronto para uso | | REJECTED | Rejeitado na revisão. Não pode ser enviado. | | PAUSED | Pausado automaticamente por baixa qualidade | | DISABLED | Desativado permanentemente | | IN_APPEAL | Em processo de apelação |

APPROVED → PAUSED

Essa transição acontece automaticamente quando o Meta detecta que a taxa de bloqueio/denúncia associada ao template está alta. O critério considera:

  • O volume de mensagens enviadas pelo template
  • A proporção de usuários que bloquearam ou denunciaram após receber

O template pode ser pausado temporariamente (e reativado automaticamente se a qualidade se recuperar) ou pausado indefinidamente até que você tome uma ação.

PAUSED → DISABLED

Se o template ficar em PAUSED por um período prolongado sem melhora de qualidade, ou se houver múltiplas pausas recorrentes, o Meta pode mover para DISABLED. Nesse estado:

  • O template não pode ser enviado
  • Não pode ser editado
  • Pode ser excluído, mas o nome fica bloqueado por 30 dias

APPROVED → REJECTED (Pós-aprovação)

Sim, isso existe. O Meta pode revisar templates já aprovados e revogar a aprovação se detectar uso inconsistente com o conteúdo declarado — por exemplo, um template de UTILITY sendo usado para enviar promoções.

Por Que Templates São Rejeitados

A rejeição acontece na fase de revisão (status PENDING). O Meta retorna um rejection_reason que indica a categoria do problema.

Principais Motivos de Rejeição

INVALID_FORMAT Problema estrutural no JSON: variáveis sem índice sequencial, exemplos ausentes, componentes inválidos para a categoria.

// ❌ Variável {{2}} sem {{1}} antes
{
  "type": "BODY",
  "text": "Seu pedido {{2}} foi enviado."
}

// ✅ Correto
{
  "type": "BODY",
  "text": "Olá, {{1}}! Seu pedido {{2}} foi enviado.",
  "example": {
    "body_text": [["João", "BR-98765"]]
  }
}

SCAM Conteúdo que o Meta identifica como potencial fraude: promessas de prêmios, links suspeitos, solicitação de dados sensíveis.

ABUSIVE_CONTENT Linguagem ofensiva, conteúdo adulto ou qualquer coisa que viole os termos de serviço do WhatsApp.

PROMOTIONAL Template submetido como UTILITY ou AUTHENTICATION com conteúdo claramente promocional. O Meta reclassifica ou rejeita.

INCORRECT_CATEGORY A categoria declarada não corresponde ao conteúdo. Diferente de PROMOTIONAL, aqui pode ser qualquer direção.

NONE Rejeição sem razão especificada. Frustrante, mas acontece — geralmente em revisões automatizadas que identificaram um padrão sem conseguir categorizar.

Consultando o Motivo de Rejeição via API

curl "https://graph.facebook.com/v21.0/SEU_WABA_ID/message_templates?name=nome_do_template&fields=name,status,rejected_reason" \
  -H "Authorization: Bearer SEU_TOKEN"
{
  "data": [
    {
      "name": "oferta_especial",
      "status": "REJECTED",
      "rejected_reason": "PROMOTIONAL",
      "id": "123456789012345"
    }
  ]
}

Monitorando Qualidade via API

Quality Rating do Número de Telefone

curl "https://graph.facebook.com/v21.0/SEU_PHONE_NUMBER_ID?fields=quality_rating,messaging_limit_tier" \
  -H "Authorization: Bearer SEU_TOKEN"
{
  "quality_rating": "GREEN",
  "messaging_limit_tier": "TIER_1K",
  "id": "SEU_PHONE_NUMBER_ID"
}

Os valores possíveis de quality_rating: GREEN, YELLOW, RED, UNKNOWN.

Os valores de messaging_limit_tier:

  • TIER_NOT_SET — Não verificado: 250 conversas/24h
  • TIER_50 — 50 conversas/24h (nova conta, ainda não escalada)
  • TIER_1K — 1.000 conversas/24h
  • TIER_10K — 10.000 conversas/24h
  • TIER_100K — 100.000 conversas/24h
  • TIER_UNLIMITED — Sem limite

Listando Templates com Status de Qualidade

curl "https://graph.facebook.com/v21.0/SEU_WABA_ID/message_templates?fields=name,status,quality_score,rejected_reason" \
  -H "Authorization: Bearer SEU_TOKEN"
{
  "data": [
    {
      "name": "notificacao_pedido",
      "status": "APPROVED",
      "quality_score": {
        "score": "GREEN",
        "date": 1709164800,
        "reasons": []
      },
      "id": "111111111111111"
    },
    {
      "name": "oferta_semana",
      "status": "PAUSED",
      "quality_score": {
        "score": "RED",
        "date": 1709078400,
        "reasons": ["BLOCKED"]
      },
      "id": "222222222222222"
    }
  ]
}

O campo quality_score.reasons pode conter: BLOCKED, REPORTED, READ (todos negativos quando presentes).

Script de Auditoria em Node.js

const axios = require('axios')

const WABA_ID = process.env.WHATSAPP_WABA_ID
const PHONE_ID = process.env.WHATSAPP_PHONE_NUMBER_ID
const TOKEN = process.env.WHATSAPP_TOKEN

async function auditTemplateHealth() {
  // Verificar rating do número
  const phoneRes = await axios.get(
    `https://graph.facebook.com/v21.0/${PHONE_ID}`,
    {
      params: {
        fields: 'quality_rating,messaging_limit_tier,display_phone_number',
      },
      headers: { Authorization: `Bearer ${TOKEN}` },
    }
  )

  const phone = phoneRes.data
  console.log(`\n📱 Número: ${phone.display_phone_number}`)
  console.log(`⭐ Quality Rating: ${phone.quality_rating}`)
  console.log(`📊 Tier de envio: ${phone.messaging_limit_tier}`)

  // Verificar templates
  const templatesRes = await axios.get(
    `https://graph.facebook.com/v21.0/${WABA_ID}/message_templates`,
    {
      params: {
        fields: 'name,status,quality_score,rejected_reason,language',
        limit: 100,
      },
      headers: { Authorization: `Bearer ${TOKEN}` },
    }
  )

  const templates = templatesRes.data.data
  const byStatus = templates.reduce((acc, t) => {
    acc[t.status] = (acc[t.status] || [])
    acc[t.status].push(t)
    return acc
  }, {})

  console.log(`\n📋 Templates por status:`)
  for (const [status, list] of Object.entries(byStatus)) {
    const emoji =
      status === 'APPROVED'
        ? '✅'
        : status === 'PAUSED'
        ? '⏸️'
        : status === 'REJECTED'
        ? '❌'
        : status === 'DISABLED'
        ? '🚫'
        : '⏳'
    console.log(`  ${emoji} ${status}: ${list.length}`)
  }

  // Alertar problemas
  const problematic = templates.filter((t) =>
    ['PAUSED', 'REJECTED', 'DISABLED'].includes(t.status)
  )

  if (problematic.length > 0) {
    console.log(`\n⚠️ Templates com problema:`)
    for (const t of problematic) {
      console.log(`  - ${t.name} (${t.language}): ${t.status}`)
      if (t.rejected_reason) {
        console.log(`    Motivo: ${t.rejected_reason}`)
      }
      if (t.quality_score?.reasons?.length > 0) {
        console.log(`    Razões de qualidade: ${t.quality_score.reasons.join(', ')}`)
      }
    }
  }

  return { phone, templates }
}

auditTemplateHealth().catch(console.error)

Monitorando via Business Manager

Para quem prefere a interface visual, o caminho no Meta Business Manager é:

  1. Acesse business.facebook.com
  2. Vá em WhatsApp Manager (no menu lateral)
  3. Selecione sua conta WABA
  4. Clique em Message Templates na barra superior
  5. Filtre por status usando o menu dropdown no topo da lista

Para o quality rating do número:

  1. No WhatsApp Manager, vá em Phone Numbers
  2. Clique no número desejado
  3. A coluna Quality Rating mostra o status atual com o indicador colorido

O Business Manager mostra o histórico de mudanças de qualidade nos últimos 30 dias, o que é útil para correlacionar com campanhas específicas.

Webhook: Monitoramento em Tempo Real

A forma mais eficiente de reagir a mudanças de status é via webhook. O Meta envia um evento message_template_status_update sempre que um template muda de status.

Configurando o Webhook

O webhook de templates usa o mesmo endpoint que você já configurou para mensagens. Certifique-se de que o campo message_template_status_update está subscrito:

curl -X POST \
  "https://graph.facebook.com/v21.0/SEU_APP_ID/subscriptions" \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "object": "whatsapp_business_account",
    "callback_url": "https://seu-servidor.com/webhooks/whatsapp",
    "verify_token": "seu_verify_token",
    "fields": ["messages", "message_template_status_update"]
  }'

Payload do Evento

{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "SEU_WABA_ID",
      "changes": [
        {
          "value": {
            "event": "APPROVED",
            "message_template_id": 123456789012345,
            "message_template_name": "notificacao_pedido",
            "message_template_language": "pt_BR",
            "reason": null
          },
          "field": "message_template_status_update"
        }
      ]
    }
  ]
}

Para templates rejeitados, o campo reason vem preenchido:

{
  "value": {
    "event": "REJECTED",
    "message_template_id": 222222222222222,
    "message_template_name": "oferta_semana",
    "message_template_language": "pt_BR",
    "reason": "PROMOTIONAL"
  },
  "field": "message_template_status_update"
}

Para templates pausados por qualidade:

{
  "value": {
    "event": "FLAGGED",
    "message_template_id": 333333333333333,
    "message_template_name": "campanha_retencao",
    "message_template_language": "pt_BR",
    "reason": "QUALITY"
  },
  "field": "message_template_status_update"
}

Os valores possíveis do campo event:

  • APPROVED — Template aprovado
  • REJECTED — Template rejeitado
  • FLAGGED — Template pausado (quality issue)
  • DISABLED — Template desativado
  • REINSTATED — Template reativado após melhora de qualidade
  • DELETED — Template excluído

Bot de Alertas para Slack/Discord

Com o webhook configurado, o próximo passo natural é criar um bot que notifica a equipe quando um template muda de status. Aqui está uma implementação completa usando Express:

const express = require('express')
const axios = require('axios')
const crypto = require('crypto')

const app = express()

// Configuração
const config = {
  verifyToken: process.env.WHATSAPP_VERIFY_TOKEN,
  appSecret: process.env.WHATSAPP_APP_SECRET,
  slackWebhookUrl: process.env.SLACK_WEBHOOK_URL,
  discordWebhookUrl: process.env.DISCORD_WEBHOOK_URL,
}

// Middleware para parsear JSON e validar assinatura
app.use(
  '/webhooks/whatsapp',
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf
    },
  })
)

// Validação da assinatura do Meta (obrigatória em produção)
function validateSignature(req) {
  const signature = req.headers['x-hub-signature-256']
  if (!signature) return false

  const expectedSignature = crypto
    .createHmac('sha256', config.appSecret)
    .update(req.rawBody)
    .digest('hex')

  return signature === `sha256=${expectedSignature}`
}

// Formata mensagem para Slack
function formatSlackMessage(event) {
  const statusEmoji = {
    APPROVED: '✅',
    REJECTED: '❌',
    FLAGGED: '⏸️',
    DISABLED: '🚫',
    REINSTATED: '🔄',
    DELETED: '🗑️',
  }

  const emoji = statusEmoji[event.event] || '⚠️'
  const color =
    event.event === 'APPROVED' || event.event === 'REINSTATED'
      ? 'good'
      : event.event === 'FLAGGED'
      ? 'warning'
      : 'danger'

  return {
    attachments: [
      {
        color,
        title: `${emoji} Template WhatsApp: ${event.event}`,
        fields: [
          {
            title: 'Template',
            value: `\`${event.message_template_name}\``,
            short: true,
          },
          {
            title: 'Idioma',
            value: event.message_template_language,
            short: true,
          },
          {
            title: 'ID',
            value: String(event.message_template_id),
            short: true,
          },
          ...(event.reason
            ? [{ title: 'Motivo', value: event.reason, short: true }]
            : []),
        ],
        footer: 'WhatsApp Business API',
        ts: Math.floor(Date.now() / 1000),
      },
    ],
  }
}

// Formata mensagem para Discord
function formatDiscordMessage(event) {
  const statusEmoji = {
    APPROVED: '✅',
    REJECTED: '❌',
    FLAGGED: '⏸️',
    DISABLED: '🚫',
    REINSTATED: '🔄',
    DELETED: '🗑️',
  }

  const colorMap = {
    APPROVED: 0x2eb67d, // verde
    REINSTATED: 0x2eb67d,
    FLAGGED: 0xecb22e, // amarelo
    REJECTED: 0xe01e5a, // vermelho
    DISABLED: 0xe01e5a,
    DELETED: 0x808080, // cinza
  }

  const emoji = statusEmoji[event.event] || '⚠️'
  const color = colorMap[event.event] || 0x808080

  const fields = [
    { name: 'Template', value: `\`${event.message_template_name}\``, inline: true },
    { name: 'Idioma', value: event.message_template_language, inline: true },
    { name: 'ID', value: String(event.message_template_id), inline: true },
  ]

  if (event.reason) {
    fields.push({ name: 'Motivo', value: event.reason, inline: true })
  }

  return {
    embeds: [
      {
        title: `${emoji} Template WhatsApp: ${event.event}`,
        color,
        fields,
        footer: { text: 'WhatsApp Business API' },
        timestamp: new Date().toISOString(),
      },
    ],
  }
}

// Envia alertas para Slack e Discord
async function sendAlerts(event) {
  const promises = []

  if (config.slackWebhookUrl) {
    promises.push(
      axios
        .post(config.slackWebhookUrl, formatSlackMessage(event))
        .catch((err) => console.error('Erro ao enviar para Slack:', err.message))
    )
  }

  if (config.discordWebhookUrl) {
    promises.push(
      axios
        .post(config.discordWebhookUrl, formatDiscordMessage(event))
        .catch((err) => console.error('Erro ao enviar para Discord:', err.message))
    )
  }

  await Promise.allSettled(promises)
}

// Endpoint de verificação do webhook
app.get('/webhooks/whatsapp', (req, res) => {
  const mode = req.query['hub.mode']
  const token = req.query['hub.verify_token']
  const challenge = req.query['hub.challenge']

  if (mode === 'subscribe' && token === config.verifyToken) {
    console.log('Webhook verificado com sucesso')
    return res.status(200).send(challenge)
  }

  res.sendStatus(403)
})

// Endpoint de recebimento de eventos
app.post('/webhooks/whatsapp', async (req, res) => {
  // Validar assinatura em produção
  if (config.appSecret && !validateSignature(req)) {
    console.warn('Assinatura inválida recebida')
    return res.sendStatus(403)
  }

  // Responder 200 imediatamente (o Meta exige resposta rápida)
  res.sendStatus(200)

  // Processar em background
  try {
    const { entry } = req.body

    for (const ent of entry || []) {
      for (const change of ent.changes || []) {
        if (change.field === 'message_template_status_update') {
          const event = change.value
          console.log('Template status update:', JSON.stringify(event, null, 2))

          // Alertar apenas para eventos não-triviais
          const alertEvents = ['REJECTED', 'FLAGGED', 'DISABLED', 'REINSTATED']
          if (alertEvents.includes(event.event)) {
            await sendAlerts(event)
          }
        }
      }
    }
  } catch (error) {
    console.error('Erro ao processar webhook:', error)
  }
})

app.listen(3000, () => {
  console.log('Webhook server rodando na porta 3000')
})

Variáveis de Ambiente Necessárias

WHATSAPP_VERIFY_TOKEN=seu_token_de_verificacao
WHATSAPP_APP_SECRET=seu_app_secret_do_meta
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/XXX/YYY

Como Apelar Uma Rejeição

Se um template foi rejeitado e você discorda do motivo, é possível apelar. O processo é via API:

curl -X POST \
  "https://graph.facebook.com/v21.0/ID_DO_TEMPLATE" \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message_send_ttl_seconds": -1
  }'

Na prática, a apelação mais eficiente é resubmeter o template corrigido. O motivo: o processo de apelação formal pode levar dias e raramente reverte rejeições de conteúdo genuinamente problemático.

A estratégia mais eficaz:

  1. Leia o rejection_reason — mesmo que seja genérico, dá a direção
  2. Revise o conteúdo com base nas políticas do Meta para templates
  3. Corrija e resubmeta — não é necessário criar um novo template (apenas edite)
  4. Se a rejeição for NONE — tente simplificar o template ao máximo e resubmeter

Importante: Você pode editar um template rejeitado. Acesse POST /ID_DO_TEMPLATE com o JSON dos componentes corrigidos. O template volta para PENDING e segue para revisão novamente.

Editando um Template Rejeitado

curl -X POST \
  "https://graph.facebook.com/v21.0/ID_DO_TEMPLATE" \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "components": [
      {
        "type": "BODY",
        "text": "Olá, {{1}}! Seu agendamento de {{2}} foi confirmado para {{3}}. Para remarcar, acesse: {{4}}",
        "example": {
          "body_text": [["Ana Lima", "consulta médica", "15/03/2026 às 14h", "https://agendamento.exemplo.com.br/remarcar"]]
        }
      },
      {
        "type": "FOOTER",
        "text": "Responda CANCELAR para desistir do agendamento."
      }
    ]
  }'

Como Recuperar um Template Pausado

Quando um template está em PAUSED, você tem três caminhos:

1. Aguardar Recuperação Automática

Se a taxa de bloqueios cair naturalmente (porque você parou de enviar ou melhorou o targeting), o Meta pode reativar o template automaticamente. O evento REINSTATED chega via webhook.

Isso pode levar de 24h a 7 dias dependendo do volume anterior.

2. Editar o Template

Editar o conteúdo move o template de volta para PENDING. Após aprovação, o histórico de qualidade é parcialmente resetado — o template começa com um score mais limpo.

Essa é a melhor opção quando o problema é o conteúdo em si (linguagem muito agressiva, frequência implícita, etc.).

async function resumeTemplate(templateId, newComponents) {
  const response = await axios.post(
    `https://graph.facebook.com/v21.0/${templateId}`,
    { components: newComponents },
    {
      headers: {
        Authorization: `Bearer ${process.env.WHATSAPP_TOKEN}`,
        'Content-Type': 'application/json',
      },
    }
  )
  // Status volta para PENDING após a edição
  console.log('Template enviado para revisão:', response.data)
  return response.data
}

3. Excluir e Recriar

Opção nuclear — use apenas se o template precisa de um novo nome ou se a situação está irreversível. Lembre que o nome fica bloqueado por 30 dias após a exclusão.

Reestruturando Conteúdo para Melhorar o Rating

A causa mais comum de templates pausados é o conteúdo ser percebido como spam pelos destinatários. Aqui está o que funciona na prática:

Segmente a Audiência

Enviar para toda a base ao mesmo tempo é a receita para altas taxas de bloqueio. Usuários inativos há meses tendem a bloquear quando recebem mensagens inesperadas.

Antes: Disparo para 50.000 contatos de uma vez. Depois: Disparo em lotes de 5.000, priorizando contatos ativos nos últimos 30 dias.

Personalize as Variáveis de Verdade

Templates com variáveis genéricas são indistinguíveis de spam. Use dados reais que fazem sentido para aquele usuário.

❌ "Olá, cliente! Temos uma oferta especial para você."
✅ "Olá, {{1}}! Vimos que você comprou {{2}} há 30 dias. Que tal reabastecer?"

Adicione Contexto de Por Que Está Enviando

Usuários bloqueiam quando não entendem por que estão recebendo aquela mensagem.

❌ "Aproveite: 20% OFF em todos os produtos."
✅ "Você comprou conosco em {{1}} e habilitou notificações de promoção. Hoje temos: {{2}}"

Respeite o Opt-Out

Incluir instrução de cancelamento não é só boa prática — é obrigatório para marketing. E mais importante: honre o opt-out imediatamente. Enviar para quem pediu para sair é a forma mais rápida de acumular bloqueios.

Reduza a Frequência

Mais de uma mensagem de marketing por semana para o mesmo usuário é território perigoso. Para notificações transacionais, o limite é mais alto, mas ainda existe.

Resumo Operacional

O ciclo de vida dos templates é contínuo — não termina na aprovação. Os pontos mais críticos para manter a saúde dos seus templates em produção:

  1. Configure o webhook message_template_status_update — é a única forma de saber em tempo real quando algo muda
  2. Monitore o quality rating do número semanalmente — não espere o rating virar RED para agir
  3. Segmente antes de disparar — prefira audiências menores e mais qualificadas a listas completas
  4. Edite templates problemáticos em vez de excluir — preserva o histórico e não bloqueia o nome
  5. Para rejeições com reason: "NONE" — simplifique o conteúdo e resubmeta

Com monitoramento proativo e atenção ao comportamento dos destinatários, a maioria dos problemas de qualidade é evitável antes de chegar ao PAUSED.

Artigos Relacionados

  • APIs & Integrações

    Como Criar e Gerenciar Templates na WhatsApp Business API: Guia Completo

  • APIs & Integrações

    Como Enviar Imagens, Vídeos e Documentos pela WhatsApp Business API

  • APIs & Integrações

    Como Configurar Webhooks na WhatsApp Business API: Guia Técnico Completo

.