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:
- Ao criar a instância.
- 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 |