Conceitos Fundamentais

Entenda o modelo, as limitações, e as armadilhas reais que custem tempo na integração.

O que é Coexistência?

Coexistência significa que o cliente continua usando o app WhatsApp Business normalmente no celular e ao mesmo tempo você controla a API.

Seu CRM, ERP, Automação
↕
API VZD Application
↕
WhatsApp Business Platform
↕
Celular do Cliente (App WhatsApp Business)

Alternativas:

  • Cloud API Only: API só, celular desligado. Mais simples, menos controle.
  • Coexistence: (recomendado) API + celular. Cliente atende enquanto você automatiza.

A Janela de 24 Horas

Pela política da Meta, texto livre só passa dentro de 24 horas desde a última mensagem do cliente final.

CenárioResultado
Cliente mandou "Olá" às 10h. Você responde às 11h com texto livre.✓ Passa
Cliente mandou "Olá" às 10h. Você responde 25 horas depois com texto livre.✗ Falha (erro 131042)
Você envia template FORA da janela (dia seguinte).✓ Passa (templates não têm limite)
Agendamento para amanhã: template ou texto fora da janela?Sempre fora — use template
Armadilha: Lembretes de agendamento são o caso de uso mais comum e SEMPRE caem fora da janela de 24h. Use template desde o começo, não texto livre.
Dica: Se o cliente responder algo (mesmo emoji), a janela se renova por mais 24h. Conversas ativas têm janela indefinida.

Template: A Ferramenta Obrigatória

Template é mensagem pré-aprovada pela Meta. Qualquer uso fora da janela de 24h requer template.

Estados de um template

EstadoPode enviar?Nota
APPROVED✓ SimPronto para envio
PENDING_REVIEW✗ NãoAguardando aprovação da Meta (minutos até horas)
REJECTED✗ NãoMeta recusou — motivo em rejection_reason
PAUSED✗ NãoMeta pausou por qualidade — ainda há erro 131042 ao tentar
DISABLED✗ NãoVocê desabilitou ou Meta desabilitou
LIMIT_EXCEEDED✗ NãoConta atingiu limite de templates
Armadilha #1: Um template APPROVED pode virar PAUSED depois sem aviso — a Meta ativa sistema de controle de qualidade. Você vai receber webhook whatsapp.template.status com o novo estado.
Armadilha #2: Se apagar um template, o nome fica bloqueado por 30 dias. Use versionamento desde o início: confirmacao_v1, confirmacao_v2, etc. Assim você cria nova versão sem esperar.
Armadilha #3: Eventos de mudança de estado do template não carregam phone_number_id — só waba_id. Se sua projeção chavear só por ID, você perde o evento em silêncio. Use (template_name, language) como chave dentro de uma WABA.

Quem Paga: O Cliente Final Paga à Meta

A mensagem é cobrada pela Meta direto da conta de WhatsApp Business do cliente final.

Cada mensagem enviada desce de um crédito pré-pago no cartão do cliente final, registrado na conta de WhatsApp Business dele.

Cenários:

  • Sem cartão vinculado: Envio de template falha com erro 131042. Não é problema da sua API — é do cliente.
  • Crédito zerado: Falha 131042 novamente. O cliente precisa recarregar.
  • Conta suspensa: Falha com código de revogação (190, 200, 10, 2500).
Você não está no meio do pagamento: VZD não é gateway de pagamento. O cliente paga a Meta, e Meta desvia crédito por cada envio. VZD opera apenas a API.
Armadilha: Se o cliente disser "envio não funciona", a primeira coisa a verificar é se ele tem cartão vinculado e crédito. 90% dos casos é isso.

Echo vs. Sent: A Diferença Crítica

Em Coexistência, há dois tipos de outbound (mensagens saindo):

TipooriginQuem enviou?Ação recomendada
EchoBUSINESS_APP_ECHOAlguém respondeu pelo celular (app WhatsApp Business)Registrar no CRM, não enviar para IA
SentCLOUD_APIVocê enviou via APIRegistrar como "seu"

Sem filtrar echo, sua IA pode responder a uma resposta que o próprio time deu pelo celular — criando loop.

// ERRADO: sem filtro
if (event.event === 'whatsapp.message.received') {
  await aiService.respond(event.data); // Vai responder ao echo também!
}

// CERTO: filtra echo
if (event.event === 'whatsapp.message.received' &&
    event.data.origin !== 'BUSINESS_APP_ECHO') {
  await aiService.respond(event.data); // Só mensagens reais do cliente
}
      

Estados do Canal

StatusSignificado
CONNECTEDTudo ok. Pode enviar e receber.
DEGRADEDConectado mas com aviso (ex: webhook não registrado). Funciona, mas incompleto.
REVOKEDCliente revogou permissão no Gerenciador de Negócios da Meta. Não funciona mais — avise o cliente.

Limite de Templates por Conta

Cada conta WhatsApp Business tem teto de templates. Limite padrão: 250 por idioma.

Quando atinge o limite, novos templates ficam em LIMIT_EXCEEDED e não podem ser enviados.

Comportamento de nome duplicado: Se tentar criar template com nome idêntico a um existente, a Meta pode recusar ou retornar o template existente — comportamento não garantido. Por isso use versionamento: template_v1, template_v2.

Assinatura de Webhook (HMAC-SHA256)

Toda mensagem que VZD envia para seu webhook está assinada com HMAC-SHA256.

  • Header: X-VZD-Signature
  • Formato: sha256=<hash_hex>
  • Computado sobre: O corpo bruto da requisição (bytes exatos)
  • Chave: Seu segredo de webhook (armazenado cifrado em repouso)

Sempre valide a assinatura antes de processar. É a única forma de garantir que veio da VZD e não de um atacante.

Reserializar o JSON quebra o HMAC. Se você fizer JSON.stringify(request.body) para re-validar, a ordem de chaves e espaçamento podem mudar. Use sempre o corpo bruto original.

Deduplicação: event_id

Se VZD não receber confirmação (HTTP 200) rápido, reenvia o mesmo evento com o mesmo event_id.

Para não processar duplicatas:

  1. Registre cada event_id que processar
  2. Se receber novamente, responda 200 imediatamente
  3. Guarde registro por pelo menos 7 dias

event_id é determinístico por evento — sempre o mesmo para a mesma mensagem.

Criptografia em Repouso

Sua chave de API e seu segredo de webhook são cifrados em Postgres com AES-256-GCM.

Dump do banco não entrega suas credenciais — teria que quebrar a cifra.

Cada secret tem um ID de chave embutido no envelope (formato v2.k1.<iv>.<tag>.<ciphertext>), permitindo girar chaves sem parada de serviço.

Rate Limiting

LimiteEscopoPadrão
Envio de mensagensPor canal (phone_number_id), por segundoConfira documentação
Listagem de templatesPor canal, por minuto30 chamadas/min
Criação de sessão de conexãoPor tenant, por janelaConfira documentação

Se exceder, recebe HTTP 429 com header Retry-After: <segundos>.

Ataque de loop de listagem: Seu frontend faz GET /templates a cada keypress? Isso queima a quota de envio. Aplique debounce (esperar 500ms) e cache local.

Checklist antes de ir a produção