Integração WhatsApp via Evolution API
A Evolution API permite que você envie notificações, alertas e relatórios diretamente para WhatsApp quando suas tarefas são executadas, proporcionando comunicação em tempo real.
Visão Geral
A integração com Evolution API permite:
- Enviar notificações instantâneas via WhatsApp
- Alertar sobre conclusão ou falha de tarefas
- Enviar relatórios e resumos
- Suportar mensagens de texto, imagens e documentos
- Criar bots interativos (opcional)
Pré-requisitos
- Servidor com Evolution API instalada (próprio ou terceirizado)
- WhatsApp Business ou pessoal para conectar
- Variáveis de ambiente configuradas
Opções de Instalação
Opção 1: Usar Serviço Terceirizado (Recomendado)
Provedores que oferecem Evolution API hospedada:
| Provedor | Preço | Link |
|---|---|---|
| Hocketzap | R$ 29/mês | hocketzap.com |
| Zapclouds | R$ 39/mês | zapclouds.com.br |
| ChatWoot | R$ 49/mês | chatwoot.com |
Vantagens:
- Configuração rápida (5 minutos)
- Sem gerenciamento de servidor
- Suporte técnico incluído
- Backups automáticos
Opção 2: Hospedar Própria Evolution API
Se preferir hospedar sua própria instância:
# 1. Criar docker-compose.yml
cat > docker-compose.yml <<EOF
version: '3.8'
services:
evolution-api:
image: atendai/evolution-api:v2.0.0
container_name: evolution_api
restart: always
ports:
- "8080:8080"
environment:
# Servidor
- SERVER_URL=https://seu-dominio.com
- SERVER_PORT=8080
# Database (PostgreSQL)
- DATABASE_ENABLED=true
- DATABASE_PROVIDER=postgresql
- DATABASE_CONNECTION_URI=postgresql://user:password@postgres:5432/evolution
# Autenticação
- AUTHENTICATION_API_KEY=sua-chave-secreta-aqui
# Webhook (opcional)
- WEBHOOK_GLOBAL_URL=https://seu-site.com/api/webhooks/whatsapp
# Storage (opcional - S3, MinIO, etc)
- STORAGE_PROVIDER=local
volumes:
- evolution_instances:/evolution/instances
depends_on:
- postgres
postgres:
image: postgres:15-alpine
container_name: postgres
restart: always
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=evolution
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
evolution_instances:
postgres_data:
EOF
# 2. Iniciar serviços
docker-compose up -d
# 3. Verificar logs
docker-compose logs -f evolution-apiRequisitos do servidor:
- 2GB RAM mínimo
- 20GB disco
- Porta 8080 aberta
- Domínio com SSL (Nginx + Let's Encrypt)
# /etc/nginx/sites-available/evolution-api
server {
listen 80;
server_name api.seudominio.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.seudominio.com;
ssl_certificate /etc/letsencrypt/live/api.seudominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.seudominio.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}# Obter certificado SSL
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d api.seudominio.comConfiguração Passo a Passo
1. Obter Credenciais da Evolution API
Se estiver usando serviço terceirizado:
- Cadastre-se no provedor escolhido
- Crie uma nova instância
- Copie a API URL e API Key
Se estiver auto-hospedando:
- API URL:
https://seu-dominio.com - API Key: A que você definiu em
AUTHENTICATION_API_KEY
2. Criar Instância do WhatsApp
Via API:
curl -X POST 'https://sua-evolution-api.com/instance/create' \
-H 'Content-Type: application/json' \
-H 'apikey: sua-api-key' \
-d '{
"instanceName": "tarefaai",
"qrcode": true,
"integration": "WHATSAPP-BAILEYS"
}'Resposta:
{
"instance": {
"instanceName": "tarefaai",
"status": "created"
},
"qrcode": {
"code": "data:image/png;base64,iVBORw0KG..."
}
}3. Conectar WhatsApp (QR Code)
Escaneie o QR Code retornado:
- Abra o WhatsApp no celular
- Vá em Configurações → Aparelhos Conectados
- Toque em Conectar Aparelho
- Escaneie o QR Code retornado pela API
Importante: O QR Code expira em 60 segundos. Se expirar, gere um novo fazendo outra requisição.
4. Verificar Conexão
curl -X GET 'https://sua-evolution-api.com/instance/connectionState/tarefaai' \
-H 'apikey: sua-api-key'Resposta esperada:
{
"instance": {
"instanceName": "tarefaai",
"state": "open"
}
}Estados possíveis:
open✅ Conectadoconnecting⏳ Conectandoclose❌ Desconectado
5. Configurar Variáveis de Ambiente
Desenvolvimento Local (.env.local)
# Evolution API Configuration
EVOLUTION_API_URL=https://sua-evolution-api.com
EVOLUTION_API_KEY=sua-api-key-aqui
EVOLUTION_INSTANCE_NAME=tarefaai
# Optional: Phone number for admin notifications
EVOLUTION_ADMIN_PHONE=5511999999999Produção (Vercel/Railway/Render)
Vercel:
vercel env add EVOLUTION_API_URL
vercel env add EVOLUTION_API_KEY
vercel env add EVOLUTION_INSTANCE_NAMERailway:
railway variables set EVOLUTION_API_URL=https://sua-api.com
railway variables set EVOLUTION_API_KEY=sua-chave
railway variables set EVOLUTION_INSTANCE_NAME=tarefaaiDocker:
# docker-compose.yml
environment:
- EVOLUTION_API_URL=${EVOLUTION_API_URL}
- EVOLUTION_API_KEY=${EVOLUTION_API_KEY}
- EVOLUTION_INSTANCE_NAME=${EVOLUTION_INSTANCE_NAME}6. Verificar Instalação
Teste enviando uma mensagem:
// scripts/test-whatsapp.ts
async function testWhatsApp() {
const response = await fetch(
`${process.env.EVOLUTION_API_URL}/message/sendText/${process.env.EVOLUTION_INSTANCE_NAME}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': process.env.EVOLUTION_API_KEY!,
},
body: JSON.stringify({
number: '5511999999999', // Seu número
text: 'Teste de integração Evolution API ✅',
}),
}
);
const result = await response.json();
console.log('Mensagem enviada:', result);
}
testWhatsApp();Execute:
npm run test:whatsapp
# ou
npx tsx scripts/test-whatsapp.tsExemplos Práticos
Exemplo 1: Notificação Simples de Tarefa
// src/lib/whatsapp/send-notification.ts
export async function sendWhatsAppNotification({
phoneNumber,
message,
}: {
phoneNumber: string;
message: string;
}) {
const url = `${process.env.EVOLUTION_API_URL}/message/sendText/${process.env.EVOLUTION_INSTANCE_NAME}`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': process.env.EVOLUTION_API_KEY!,
},
body: JSON.stringify({
number: phoneNumber,
text: message,
}),
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.message || 'Erro ao enviar mensagem');
}
return {
success: true,
messageId: result.key?.id,
};
} catch (error) {
console.error('Erro ao enviar WhatsApp:', error);
return {
success: false,
error: error.message,
};
}
}
// Uso em tarefa
await sendWhatsAppNotification({
phoneNumber: '5511999999999',
message: `Tarefa "${taskName}" concluída com sucesso! ✅`,
});Exemplo 2: Mensagem Formatada
// src/lib/whatsapp/formatted-message.ts
export async function sendTaskCompletedMessage({
phoneNumber,
taskName,
result,
duration,
}: {
phoneNumber: string;
taskName: string;
result: string;
duration: number;
}) {
const message = `
🤖 *Tarefa Concluída*
📌 *Tarefa:* ${taskName}
✅ *Status:* Sucesso
⏱️ *Duração:* ${duration}ms
📅 *Data:* ${new Date().toLocaleString('pt-BR')}
📄 *Resultado:*
${result}
---
Powered by Tarefa AI
`.trim();
return sendWhatsAppNotification({ phoneNumber, message });
}Exemplo 3: Enviar Imagem
// src/lib/whatsapp/send-image.ts
export async function sendWhatsAppImage({
phoneNumber,
imageUrl,
caption,
}: {
phoneNumber: string;
imageUrl: string;
caption?: string;
}) {
const url = `${process.env.EVOLUTION_API_URL}/message/sendMedia/${process.env.EVOLUTION_INSTANCE_NAME}`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': process.env.EVOLUTION_API_KEY!,
},
body: JSON.stringify({
number: phoneNumber,
mediatype: 'image',
media: imageUrl,
caption: caption || '',
}),
});
return response.json();
}
// Uso: Enviar gráfico gerado
await sendWhatsAppImage({
phoneNumber: '5511999999999',
imageUrl: 'https://example.com/chart.png',
caption: 'Relatório de execuções - Outubro 2024',
});Exemplo 4: Enviar Documento/PDF
// src/lib/whatsapp/send-document.ts
export async function sendWhatsAppDocument({
phoneNumber,
documentUrl,
fileName,
}: {
phoneNumber: string;
documentUrl: string;
fileName: string;
}) {
const url = `${process.env.EVOLUTION_API_URL}/message/sendMedia/${process.env.EVOLUTION_INSTANCE_NAME}`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': process.env.EVOLUTION_API_KEY!,
},
body: JSON.stringify({
number: phoneNumber,
mediatype: 'document',
media: documentUrl,
fileName: fileName,
}),
});
return response.json();
}
// Uso: Enviar relatório PDF
await sendWhatsAppDocument({
phoneNumber: '5511999999999',
documentUrl: 'https://example.com/relatorio.pdf',
fileName: 'relatorio-mensal.pdf',
});Exemplo 5: Alerta de Erro para Admin
// src/lib/whatsapp/admin-alert.ts
export async function sendAdminAlert({
taskName,
error,
}: {
taskName: string;
error: Error;
}) {
const adminPhone = process.env.EVOLUTION_ADMIN_PHONE;
if (!adminPhone) return;
const message = `
🚨 *ALERTA DE ERRO*
❌ *Tarefa:* ${taskName}
⚠️ *Erro:* ${error.message}
📅 *Timestamp:* ${new Date().toLocaleString('pt-BR')}
Por favor, verifique os logs para mais detalhes.
`.trim();
await sendWhatsAppNotification({
phoneNumber: adminPhone,
message,
});
}Exemplo 6: Mensagem com Botões (Lista)
// src/lib/whatsapp/send-list.ts
export async function sendWhatsAppList({
phoneNumber,
title,
description,
buttonText,
sections,
}: {
phoneNumber: string;
title: string;
description: string;
buttonText: string;
sections: Array<{
title: string;
rows: Array<{
title: string;
description?: string;
rowId: string;
}>;
}>;
}) {
const url = `${process.env.EVOLUTION_API_URL}/message/sendList/${process.env.EVOLUTION_INSTANCE_NAME}`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': process.env.EVOLUTION_API_KEY!,
},
body: JSON.stringify({
number: phoneNumber,
title,
description,
buttonText,
sections,
}),
});
return response.json();
}
// Uso: Menu interativo
await sendWhatsAppList({
phoneNumber: '5511999999999',
title: 'Relatórios Disponíveis',
description: 'Selecione o relatório que deseja receber:',
buttonText: 'Ver Opções',
sections: [
{
title: 'Relatórios',
rows: [
{
title: 'Resumo Diário',
description: 'Últimas 24 horas',
rowId: 'daily',
},
{
title: 'Resumo Semanal',
description: 'Últimos 7 dias',
rowId: 'weekly',
},
{
title: 'Resumo Mensal',
description: 'Últimos 30 dias',
rowId: 'monthly',
},
],
},
],
});Webhooks (Receber Mensagens)
Configure webhooks para criar bots interativos:
1. Configurar Webhook na Evolution API
curl -X POST 'https://sua-evolution-api.com/webhook/set/tarefaai' \
-H 'Content-Type: application/json' \
-H 'apikey: sua-api-key' \
-d '{
"url": "https://seu-site.com/api/webhooks/whatsapp",
"webhook_by_events": false,
"webhook_base64": false,
"events": [
"MESSAGES_UPSERT",
"MESSAGES_UPDATE",
"CONNECTION_UPDATE"
]
}'2. Criar Endpoint de Webhook
// src/app/api/webhooks/whatsapp/route.ts
export async function POST(request: Request) {
const event = await request.json();
// Verificar tipo de evento
if (event.event === 'messages.upsert') {
const message = event.data.messages[0];
// Ignorar mensagens enviadas por você
if (message.key.fromMe) return new Response('OK');
const phoneNumber = message.key.remoteJid.replace('@s.whatsapp.net', '');
const messageText = message.message?.conversation ||
message.message?.extendedTextMessage?.text || '';
// Processar comandos
if (messageText.startsWith('/')) {
await handleCommand(phoneNumber, messageText);
}
}
return new Response('OK', { status: 200 });
}
async function handleCommand(phoneNumber: string, command: string) {
switch (command.toLowerCase()) {
case '/status':
const stats = await getTaskStats();
await sendWhatsAppNotification({
phoneNumber,
message: `Status das tarefas:\n\n${stats}`,
});
break;
case '/help':
await sendWhatsAppNotification({
phoneNumber,
message: `
Comandos disponíveis:
/status - Ver status das tarefas
/help - Ver esta mensagem
`.trim(),
});
break;
default:
await sendWhatsAppNotification({
phoneNumber,
message: 'Comando não reconhecido. Digite /help para ver os comandos.',
});
}
}Otimização e Boas Práticas
1. Rate Limiting
// src/lib/whatsapp/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(20, '1 m'), // 20 mensagens por minuto
});
export async function sendWithRateLimit(phoneNumber: string, message: string) {
const { success } = await ratelimit.limit(phoneNumber);
if (!success) {
throw new Error('Limite de mensagens excedido. Aguarde 1 minuto.');
}
return sendWhatsAppNotification({ phoneNumber, message });
}2. Retry com Backoff
// src/lib/whatsapp/retry.ts
export async function sendWithRetry(
data: any,
maxRetries = 3
) {
for (let i = 0; i < maxRetries; i++) {
try {
const result = await sendWhatsAppNotification(data);
return result;
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000;
console.log(`Tentativa ${i + 1} falhou. Aguardando ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}3. Validação de Número
// src/lib/whatsapp/validate.ts
export function validateBrazilianPhone(phone: string): string {
// Remove caracteres não numéricos
const cleaned = phone.replace(/\D/g, '');
// Validar formato brasileiro: 5511999999999
if (!cleaned.match(/^55\d{2}9?\d{8}$/)) {
throw new Error('Número de telefone inválido');
}
return cleaned;
}
// Uso
const phone = validateBrazilianPhone('(11) 99999-9999');
// Retorna: 55119999999994. Logging e Monitoramento
// src/lib/whatsapp/logging.ts
export async function sendAndLog(data: any) {
const result = await sendWhatsAppNotification(data);
// Salvar log no banco
await prisma.whatsAppLog.create({
data: {
phoneNumber: data.phoneNumber,
message: data.message,
status: result.success ? 'sent' : 'failed',
messageId: result.messageId,
sentAt: new Date(),
},
});
return result;
}Troubleshooting
Erro: "Instance not connected"
Causa: WhatsApp desconectado ou QR Code não escaneado
Solução:
# Verificar status da instância
curl -X GET 'https://sua-api.com/instance/connectionState/tarefaai' \
-H 'apikey: sua-api-key'
# Se desconectado, gerar novo QR Code
curl -X GET 'https://sua-api.com/instance/connect/tarefaai' \
-H 'apikey: sua-api-key'Erro: "Invalid phone number"
Causa: Formato de número incorreto
Solução:
// Formato correto: Código do país + DDD + número
// Brasil: 5511999999999 (sem +, espaços ou hífens)
// Converter de formato brasileiro
function formatPhone(phone: string): string {
const cleaned = phone.replace(/\D/g, '');
// Se não tem código do país, adicionar 55
if (!cleaned.startsWith('55')) {
return '55' + cleaned;
}
return cleaned;
}Mensagens Não Chegam
Verificações:
- Instância conectada:
curl 'https://sua-api.com/instance/connectionState/tarefaai' \
-H 'apikey: sua-api-key'
# Deve retornar "state": "open"- Número válido:
// Testar enviando para seu próprio número primeiro
await sendWhatsAppNotification({
phoneNumber: '5511999999999', // Seu número
message: 'Teste',
});- API Key correta:
echo $EVOLUTION_API_KEY
# Verificar se não tem espaços ou caracteres extrasWhatsApp Desconecta Frequentemente
Possíveis causas:
- Celular sem internet: WhatsApp no celular precisa estar conectado
- Múltiplos dispositivos: Não conecte a mesma conta em várias APIs
- Versão antiga da API: Atualize para última versão
Solução:
// Implementar reconexão automática
async function ensureConnected() {
const state = await checkConnectionState();
if (state !== 'open') {
console.log('Reconectando WhatsApp...');
await connectInstance();
}
}
// Executar antes de enviar mensagens
await ensureConnected();
await sendWhatsAppNotification(data);Erro 429: Too Many Requests
Causa: Limite de requisições excedido
Solução:
// Implementar fila de mensagens
import { Queue } from 'bull';
const whatsappQueue = new Queue('whatsapp', {
redis: process.env.REDIS_URL,
});
// Adicionar à fila ao invés de enviar diretamente
await whatsappQueue.add({
phoneNumber: '5511999999999',
message: 'Mensagem',
}, {
delay: 1000, // 1 segundo entre mensagens
});
// Processar fila
whatsappQueue.process(async (job) => {
return sendWhatsAppNotification(job.data);
});Recursos Adicionais
Documentação Oficial
Comunidade
Suporte
- Telegram: @evolutionapi_support
- Email: contato@evolution-api.com
Provedores Recomendados
Próximos Passos
Agora que você configurou WhatsApp, explore:
- Integração com Notion - Salve mensagens enviadas
- Webhooks API - Crie bots interativos
- OpenRouter AI - Adicione IA às respostas
Precisa de ajuda?
Consulte nosso guia completo de troubleshooting