WhatsApp Business API + n8n
Integração completa do WhatsApp Business API via n8n para campanhas de CRM, confirmações e bot de atendimento.
Arquitetura da Integração
sequenceDiagram
participant Admin as Painel Admin
participant API as Lambda API
participant SQS as SQS Queue
participant Worker as Messaging Worker
participant N8N as n8n
participant Meta as WhatsApp Business API
participant Client as Cliente Final
Admin->>API: Criar campanha CRM
API->>SQS: Publicar mensagens em lote
Worker->>SQS: Consumir batch (10 mensagens)
loop Para cada destinatário
Worker->>N8N: POST /webhook/send-whatsapp
N8N->>Meta: Enviar template message
Meta->>Client: Entregar WhatsApp
Meta-->>N8N: Webhook status (delivered/read/failed)
N8N-->>API: Atualizar status via callback
end
Configuração do WhatsApp Business API
1. Pré-requisitos Meta
- Meta Business Account ativo
- WhatsApp Business Account verificado
- Número de telefone verificado e aprovado
- App criado no Meta Developers Console
2. Configuração no Meta Business Manager
# 1. Criar app no Meta Developers
# 2. Adicionar produto "WhatsApp"
# 3. Configurar webhook endpoint
WEBHOOK_URL=https://bitio-api.com/webhooks/whatsapp
VERIFY_TOKEN=seu-token-verificacao-seguro
# 4. Adicionar número de telefone
PHONE_NUMBER_ID=123456789
3. Tokens e Credenciais
# Variáveis de ambiente necessárias
META_APP_ID=sua-app-id
META_APP_SECRET=seu-app-secret
WHATSAPP_TOKEN=seu-whatsapp-token-permanente
WHATSAPP_PHONE_ID=seu-phone-number-id
WHATSAPP_WEBHOOK_VERIFY_TOKEN=token-verificacao
Setup do n8n
1. Instalação via Docker
# docker-compose.yml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: bitio-n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=n8n.bitio.internal
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.bitio.com
- NODE_ENV=production
volumes:
- n8n_data:/home/node/.n8n
networks:
- bitio-network
volumes:
n8n_data:
networks:
bitio-network:
external: true
2. Configuração de Credenciais
No painel do n8n, configurar:
// WhatsApp Business Credentials
{
"name": "WhatsApp Business - Bitio",
"type": "httpHeaderAuth",
"data": {
"headerAuth": {
"name": "Authorization",
"value": "Bearer {{WHATSAPP_TOKEN}}"
}
}
}
Workflows n8n
1. Workflow: Enviar Campanha WhatsApp
{
"name": "Send WhatsApp Campaign",
"nodes": [
{
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"parameters": {
"httpMethod": "POST",
"path": "send-whatsapp",
"responseMode": "responseNode"
}
},
{
"name": "Validate Input",
"type": "n8n-nodes-base.function",
"parameters": {
"functionCode": "// Validar payload\nconst { phone, template, variables, metadata } = $input.first().json;\n\nif (!phone || !template) {\n throw new Error('Phone and template are required');\n}\n\nreturn [{\n json: {\n phone: phone.replace(/\\D/g, ''), // Limpar formatação\n template,\n variables: variables || {},\n metadata: metadata || {}\n }\n}];"
}
},
{
"name": "Send to WhatsApp API",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://graph.facebook.com/v18.0/{{WHATSAPP_PHONE_ID}}/messages",
"method": "POST",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{WHATSAPP_TOKEN}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "messaging_product",
"value": "whatsapp"
},
{
"name": "to",
"value": "={{ $json.phone }}"
},
{
"name": "type",
"value": "template"
},
{
"name": "template",
"value": "={{ JSON.stringify({\n name: $json.template,\n language: { code: 'pt_BR' },\n components: [\n {\n type: 'body',\n parameters: Object.keys($json.variables).map(key => ({\n type: 'text',\n text: $json.variables[key]\n }))\n }\n ]\n}) }}"
}
]
}
}
},
{
"name": "Update Campaign Status",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.bitio.com/v1/campaigns/update-status",
"method": "POST",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{BITIO_API_TOKEN}}"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "messageId",
"value": "={{ $json.messages[0].id }}"
},
{
"name": "status",
"value": "sent"
},
{
"name": "metadata",
"value": "={{ JSON.stringify($('Webhook Trigger').first().json.metadata) }}"
}
]
}
}
},
{
"name": "Response",
"type": "n8n-nodes-base.respondToWebhook",
"parameters": {
"responseCode": 200,
"responseBody": "={{ JSON.stringify({ success: true, messageId: $json.messages[0].id }) }}"
}
}
]
}
2. Workflow: Processar Webhooks de Status
{
"name": "WhatsApp Status Webhook",
"nodes": [
{
"name": "Webhook Listener",
"type": "n8n-nodes-base.webhook",
"parameters": {
"httpMethod": "POST",
"path": "whatsapp-status",
"responseCode": 200
}
},
{
"name": "Process Status Update",
"type": "n8n-nodes-base.function",
"parameters": {
"functionCode": "// Processar webhook da Meta\nconst data = $input.first().json;\n\n// Verificar se é status de mensagem\nif (data.entry?.[0]?.changes?.[0]?.value?.statuses) {\n const statuses = data.entry[0].changes[0].value.statuses;\n \n return statuses.map(status => ({\n json: {\n messageId: status.id,\n recipientId: status.recipient_id,\n status: status.status, // sent, delivered, read, failed\n timestamp: status.timestamp,\n error: status.errors?.[0]\n }\n }));\n}\n\nreturn [];"
}
},
{
"name": "Update Database",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.bitio.com/v1/whatsapp/update-status",
"method": "POST",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "messageId",
"value": "={{ $json.messageId }}"
},
{
"name": "status",
"value": "={{ $json.status }}"
},
{
"name": "timestamp",
"value": "={{ $json.timestamp }}"
}
]
}
}
}
]
}
Templates Aprovados
1. Template de Campanha CRM
{
"name": "campanha_crm",
"category": "MARKETING",
"language": "pt_BR",
"components": [
{
"type": "BODY",
"text": "Olá {{1}}! 🍽️\n\nTemos uma oferta especial para você: {{2}}\n\nClique aqui para ver: {{3}}\n\nVálido até amanhã!"
},
{
"type": "FOOTER",
"text": "Para parar de receber mensagens, responda PARE"
}
]
}
2. Template de Confirmação de Reserva
{
"name": "confirmacao_reserva",
"category": "UTILITY",
"language": "pt_BR",
"components": [
{
"type": "BODY",
"text": "✅ Reserva confirmada!\n\n👤 Nome: {{1}}\n📅 Data: {{2}}\n⏰ Horário: {{3}}\n🏪 Local: {{4}}\n\nNos vemos lá!"
}
]
}
3. Template de Cupom Fidelidade
{
"name": "cupom_fidelidade",
"category": "UTILITY",
"language": "pt_BR",
"components": [
{
"type": "BODY",
"text": "🎉 Parabéns {{1}}!\n\nVocê desbloqueou: {{2}}\n\nSeu cupom: *{{3}}*\n\nApresente este código no balcão para resgatar!"
}
]
}
Integração com o Backend
1. Messaging Worker (Node.js)
// messaging-worker.ts
import { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } from '@aws-sdk/client-sqs';
interface WhatsAppMessage {
customerId: string;
phone: string;
template: string;
variables: Record<string, string>;
campaignId?: string;
}
export class MessagingWorker {
private sqs = new SQSClient({ region: 'sa-east-1' });
private queueUrl = process.env.WHATSAPP_QUEUE_URL!;
private n8nWebhook = process.env.N8N_WEBHOOK_URL!;
async processMessages() {
try {
const command = new ReceiveMessageCommand({
QueueUrl: this.queueUrl,
MaxNumberOfMessages: 10,
WaitTimeSeconds: 20, // Long polling
});
const result = await this.sqs.send(command);
if (result.Messages) {
await Promise.all(
result.Messages.map(msg => this.processMessage(msg))
);
}
} catch (error) {
logger.error('Error processing messages', { error });
}
}
private async processMessage(sqsMessage: any) {
try {
const message: WhatsAppMessage = JSON.parse(sqsMessage.Body);
// Chamar n8n webhook
const response = await fetch(this.n8nWebhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: message.phone,
template: message.template,
variables: message.variables,
metadata: {
customerId: message.customerId,
campaignId: message.campaignId,
},
}),
});
if (response.ok) {
// Remover mensagem da fila se sucesso
await this.sqs.send(new DeleteMessageCommand({
QueueUrl: this.queueUrl,
ReceiptHandle: sqsMessage.ReceiptHandle,
}));
logger.info('Message sent successfully', {
customerId: message.customerId,
phone: message.phone,
});
} else {
throw new Error(`n8n webhook failed: ${response.status}`);
}
} catch (error) {
logger.error('Failed to process message', {
error,
message: sqsMessage.Body,
});
// Mensagem volta para fila (retry automático)
}
}
}
// Iniciar worker
const worker = new MessagingWorker();
setInterval(() => worker.processMessages(), 5000);
2. Webhook Handler (Lambda)
// webhook-handler.ts
import { APIGatewayProxyHandler } from 'aws-lambda';
export const whatsappWebhook: APIGatewayProxyHandler = async (event) => {
try {
// Verificar token de verificação (setup inicial)
if (event.queryStringParameters?.['hub.mode'] === 'subscribe') {
const token = event.queryStringParameters['hub.verify_token'];
if (token === process.env.WHATSAPP_VERIFY_TOKEN) {
return {
statusCode: 200,
body: event.queryStringParameters['hub.challenge'] || '',
};
}
}
// Processar webhook de status
const body = JSON.parse(event.body || '{}');
if (body.entry?.[0]?.changes?.[0]?.value?.statuses) {
await processStatusUpdates(body.entry[0].changes[0].value.statuses);
}
// Processar mensagens recebidas (inbound)
if (body.entry?.[0]?.changes?.[0]?.value?.messages) {
await processInboundMessages(body.entry[0].changes[0].value.messages);
}
return { statusCode: 200, body: 'OK' };
} catch (error) {
logger.error('Webhook error', { error });
return { statusCode: 500, body: 'Error' };
}
};
async function processStatusUpdates(statuses: any[]) {
for (const status of statuses) {
await prisma.campaignRecipient.updateMany({
where: { messageId: status.id },
data: {
status: status.status,
[`${status.status}At`]: new Date(parseInt(status.timestamp) * 1000),
},
});
}
}
Rate Limiting e Otimização
1. Limites da API WhatsApp
- 1000 mensagens por segundo por número de telefone
- Janela de conversa de 24h (cobrança por conversa, não por mensagem)
- Templates pré-aprovados obrigatórios para mensagens de marketing
2. Otimização no n8n
// Rate limiting no workflow
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Entre cada envio
await delay(100); // 10 msgs/segundo (dentro do limite)
// Retry em caso de erro 429 (rate limit)
if (response.status === 429) {
const retryAfter = response.headers['retry-after'] || 60;
await delay(retryAfter * 1000);
// Retry da mensagem
}
Monitoramento e Alertas
1. Métricas Principais
- Taxa de entrega (delivered/sent)
- Taxa de leitura (read/delivered)
- Taxa de falha (failed/sent)
- Latência média de processamento
- Volume por hora/dia
2. Alertas Críticos
- Token expirado ou inválido
- Webhook fora do ar por >5 minutos
- Fila SQS acumulando >1000 mensagens
- Taxa de falha >5% em 1 hora
- n8n workflow com erro repetido
3. Dashboard Grafana
-- Métricas de campanha (últimas 24h)
SELECT
status,
COUNT(*) as count,
(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER()) as percentage
FROM campaign_recipients
WHERE created_at >= NOW() - INTERVAL '24 hours'
GROUP BY status;
Troubleshooting
Problemas Comuns
| Erro | Causa | Solução |
|---|---|---|
| Token inválido | Token expirado/revogado | Renovar token no Meta Business |
| Template não encontrado | Nome incorreto/não aprovado | Verificar templates aprovados |
| Número inválido | Formato incorreto | Validar formato +5511999999999 |
| Rate limit | Muitas mensagens/segundo | Implementar backoff no n8n |
| Webhook não recebido | URL/token incorreto | Verificar configuração Meta |
Logs para Investigação
# Logs do n8n
docker logs bitio-n8n --tail 100 -f
# Logs do worker
journalctl -u bitio-worker -f
# Logs da Lambda (CloudWatch)
aws logs tail /aws/lambda/bitio-whatsapp-webhook --follow
Esta integração permite campanhas WhatsApp escaláveis e confiáveis, com rastreamento completo de entrega e resposta automática a interações dos clientes.