Pular para conteúdo

Evolution API — WhatsApp

O canal de WhatsApp do sistema é a Evolution API, em servidor próprio. Não é a API oficial da Meta.

Material antigo fala em Cloud API da Meta

Handoffs e apresentações mencionam WhatsApp Cloud API, janela de 24 horas e templates aprovados. Nada disso está no sistema. O contrato WhatsappGateway existe justamente para que trocar pela Cloud API seja trocar uma implementação, mas essa troca não foi feita.

O servidor é próprio e dedicado

Instalado em 31/08/2026 na mesma VPS de produção, por decisão do cliente. Versão 2.3.7, em evo.keilladepaula.com.br.

Antes disso o destino era um servidor compartilhado de terceiro. Ele deixou de ser destino por três razões, e vale entender porque elas explicam o desenho atual.

Razão Consequência
Hospedava instâncias de terceiros Nossa operação e a deles no mesmo lugar
A chave global comanda todas as instâncias Nosso sistema teria poder sobre números alheios
Cada instância tem uma só URL de webhook Repontar uma silenciaria a operação deles

Com servidor próprio, o alcance da chave global passa a ser apenas nosso, e é isso que torna aceitável o KP criar instâncias. Ver AD-012.

As duas chaves

Chave Onde mora Para que serve
Global .env, nunca no banco nem na interface Exclusivamente criar e excluir instâncias
Token por instância Banco, por instância Todo o resto da operação

Consequência aceita

Um servidor Evolution por ambiente, e trocar a chave global é deploy, não clique. Ver AD-013.

O webhook tem um destino só

Este é o ponto que mais causa confusão na operação, e merece ser lido com atenção.

O Evolution guarda uma URL de webhook por instância, instalada por POST /webhook/set/{instância}. Quem configurou por último fica com os eventos.

flowchart LR
  E[Instância no Evolution] -->|uma URL só| D[Ambiente que configurou por último]
  P[Produção] -.->|perde os eventos| E

E a configuração do webhook roda em dois momentos:

  1. Ao criar a instância.
  2. A cada pedido de QR code.

Pedir QR code no dev rouba os eventos da produção

Se dev e produção apontam para o mesmo servidor Evolution e usam uma instância com o mesmo nome, é literalmente a mesma instância. Um repareamento no dev repontou a instância para o dev, e a produção fica surda: não recebe mensagem, não grava mídia, não busca foto de contato. Para diagnosticar, consulte GET /webhook/find/{instância} e veja qual URL está instalada.

O que chega pelo webhook

Evento Para que
MESSAGES_UPSERT Mensagem recebida ou enviada
MESSAGES_UPDATE Mudança de estado da mensagem
MESSAGES_DELETE Apagamento
SEND_MESSAGE Confirmação de envio
CONNECTION_UPDATE Conexão do número
QRCODE_UPDATED Código de pareamento novo

A configuração usa byEvents: false, então uma URL única recebe tudo e discrimina por campo. E usa base64: true, que é o que faz a mídia chegar embutida no corpo.

Quando o download da mídia falha no webhook, o erro é engolido

O provedor registra e segue, e o webhook sai sem o anexo. Por isso existe a busca no provedor como rede de segurança: POST /chat/getBase64FromMediaMessage/{instância} tem uma segunda tentativa que o caminho do webhook não tem, baixando direto do servidor do WhatsApp depois de cinco segundos.

Autenticação do nosso endpoint

O Evolution não assina o corpo do webhook. O segredo vai como header configurado na instalação e volta em cada entrega, comparado com hash_equals.

Sem segredo, nada é configurado

O endpoint é público e se defende só pelo header. Configurar a instância sem segredo apontaria o provedor para uma porta que recusaria todas as entregas, e o sintoma seria "as mensagens simplesmente não chegam". Falhar alto na instalação é o que transforma isso em algo corrigível.

Envelopes que escondem o conteúdo

Quatro envelopes guardam o conteúdo real um nível abaixo: ephemeralMessage, documentWithCaptionMessage, viewOnceMessage e viewOnceMessageV2. Documento com legenda, que é o caso comum, é um deles. Processador que não desembrulha produz bolha vazia.

Nota de voz

POST /message/sendWhatsAppAudio/{instância} com encoding ligado faz o provedor converter com ffmpeg embutido e enviar como nota de voz de verdade. Falha de conversão vira erro do provedor, nunca envio silencioso de outra coisa.

Limites conhecidos

Limite Detalhe
Transcrição de áudio Não existe; a VPS não comporta o modelo
Envio de vídeo Não implementado
Timeout esporádico na foto de perfil Acontece; o job trata em silêncio e tenta na próxima mensagem
Mídia antiga O provedor não guarda mídia de meses atrás, então bolha vazia já gravada não se recupera