Integração WhatsApp (Evolution API)

Configure notificações via WhatsApp com Evolution API para suas tarefas agendadas

Nesta Página

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:

ProvedorPreçoLink
HocketzapR$ 29/mêshocketzap.com
ZapcloudsR$ 39/mêszapclouds.com.br
ChatWootR$ 49/mêschatwoot.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-api

Requisitos 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.com

Configuração Passo a Passo

1. Obter Credenciais da Evolution API

Se estiver usando serviço terceirizado:

  1. Cadastre-se no provedor escolhido
  2. Crie uma nova instância
  3. 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:

  1. Abra o WhatsApp no celular
  2. Vá em Configurações → Aparelhos Conectados
  3. Toque em Conectar Aparelho
  4. Escaneie o QR Code retornado pela API

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 ✅ Conectado
  • connecting ⏳ Conectando
  • close ❌ 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=5511999999999

Produção (Vercel/Railway/Render)

Vercel:

vercel env add EVOLUTION_API_URL
vercel env add EVOLUTION_API_KEY
vercel env add EVOLUTION_INSTANCE_NAME

Railway:

railway variables set EVOLUTION_API_URL=https://sua-api.com
railway variables set EVOLUTION_API_KEY=sua-chave
railway variables set EVOLUTION_INSTANCE_NAME=tarefaai

Docker:

# 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.ts

Exemplos 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: 5511999999999

4. 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:

  1. Instância conectada:
curl 'https://sua-api.com/instance/connectionState/tarefaai' \
  -H 'apikey: sua-api-key'
# Deve retornar "state": "open"
  1. Número válido:
// Testar enviando para seu próprio número primeiro
await sendWhatsAppNotification({
  phoneNumber: '5511999999999', // Seu número
  message: 'Teste',
});
  1. API Key correta:
echo $EVOLUTION_API_KEY
# Verificar se não tem espaços ou caracteres extras

WhatsApp Desconecta Frequentemente

Possíveis causas:

  1. Celular sem internet: WhatsApp no celular precisa estar conectado
  2. Múltiplos dispositivos: Não conecte a mesma conta em várias APIs
  3. 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

Provedores Recomendados


Próximos Passos

Agora que você configurou WhatsApp, explore:

Precisa de ajuda?

Consulte nosso guia completo de troubleshooting

Learn more