Pular para o conteúdo

Integração Evolution API

Provider de WhatsApp para clientes Kommo. Self-hosted, free, alta flexibilidade.


O que é Evolution API

API REST que se conecta ao WhatsApp via Baileys (biblioteca não-oficial). Permite:

  • Múltiplas instâncias (uma por tenant)
  • Envio e recepção de mensagens, mídia, status
  • Webhooks para eventos
  • Controle de conexão via QR code

Trade-off: não é oficial WhatsApp Business API. Risco de banimento se mal configurado. Aceitável para volumes moderados.


Hosting

Triadeflow hospeda própria instância de Evolution em Hetzner:

  • URL: https://evolution.triadeflow.com
  • API Key global em vault
  • Cada tenant tem instância isolada

Operações principais

Criar instância

from triadeflow_core.messaging.evolution import EvolutionProvider
provider = EvolutionProvider(
base_url="https://evolution.triadeflow.com",
api_key=settings.evolution_api_key,
)
await provider.create_instance(
instance_name=f"{tenant_id}-prod",
webhook_url=f"https://{agent_type}.triadeflow.com/webhook/{tenant_id}",
webhook_events=["MESSAGES_UPSERT", "CONNECTION_UPDATE", "QRCODE_UPDATED"],
)

Conectar (QR code)

qr = await provider.get_qr(instance_name=f"{tenant_id}-prod")
# Enviar QR code para o cliente escanear via WhatsApp Web

Cliente escaneia, instância fica connected. A partir daí, mensagens fluem.

Enviar mensagem de texto

await provider.send_text(
instance_name=f"{tenant_id}-prod",
to="5585999998888",
text="Olá! Como posso ajudar?",
)

Enviar mídia

await provider.send_media(
instance_name=f"{tenant_id}-prod",
to="5585999998888",
media_url="https://triadeflow.com/catalogo.pdf",
caption="Catálogo completo",
media_type="document",
)

Receber mensagem (webhook)

Evolution envia POST para webhook_url configurado. Payload:

{
"event": "messages.upsert",
"instance": "usa-salus-prod",
"data": {
"key": {
"remoteJid": "5585999998888@s.whatsapp.net",
"fromMe": false,
"id": "ABC123"
},
"message": {
"conversation": "Olá, quero saber sobre o serviço"
},
"messageTimestamp": 1714867200,
"pushName": "João da Silva"
}
}

Handler no agente:

@app.post("/webhook/{tenant_id}")
async def handle_webhook(tenant_id: str, payload: dict):
if payload["event"] != "messages.upsert":
return {"status": "ignored"}
if payload["data"]["key"]["fromMe"]:
return {"status": "outbound, ignored"}
# Processa mensagem
await process_incoming(tenant_id, payload)

Boas práticas para evitar banimento

WhatsApp não oficial é vulnerável a banimentos. Reduzir risco:

Aquecimento

  • Conta nova: limite primeiros 7 dias a 50 msg/dia
  • Aumente progressivamente
  • Use número verificado (Business)

Comportamento humano

  • Não responda em <1 segundo (parece bot)
  • Adicione delay aleatório entre 1-3s
  • Use “digitando…” (presence)

Limites diários

limites:
max_messages_per_day: 1000
max_messages_per_minute: 20
delay_between_messages_sec: [1, 3] # min, max aleatório

Proteções automáticas

  • Se receber account_blocked ou connection_close repetidos, pausar instância
  • Alerta no Telegram para Alex

Múltiplas instâncias por tenant

Caso de uso: tenant tem volume alto e quer dois números.

messaging:
provider: evolution
instances:
- name: usa-salus-prod-1
role: primary
- name: usa-salus-prod-2
role: secondary
routing: round_robin # ou by_segment

Migração para WhatsApp Business API oficial

Quando cliente cresce e quer estabilidade total:

  1. Solicitar verificação Meta Business
  2. Onboarding API oficial via Meta Cloud ou BSP
  3. Manter Evolution em paralelo durante transição
  4. Migrar conversas históricas

Documentação detalhada em .claude/integrations/whatsapp-business-cloud.md (futura).


Erros comuns

Instância “disconnected” do nada

Geralmente celular reiniciou ou desconectou WhatsApp Web. Pedir ao cliente reconectar.

Mensagens não chegam no webhook

Verificar:

  • URL acessível externamente (não localhost)
  • HTTPS válido
  • Eventos corretos configurados
  • Rate limit do servidor não excedido

Mensagens duplicadas

Adicionar idempotency check usando data.key.id como deduplication key (Redis SET com TTL 5min).


Hospedagem alternativa

Se Triadeflow self-hosted estiver instável, alternativas:

  • Evolution API hosted (paid SaaS)
  • Z-API (provider brasileiro)
  • WPPConnect (similar Evolution)

A interface WhatsAppProvider no core permite trocar implementação sem alterar código de domínio.


Versão: 1.0