Webhook é a forma mais eficiente de receber mensagens WhatsApp em tempo real. Em vez de ficar fazendo polling na API a cada poucos segundos — desperdiçando recursos e adicionando latência — o webhook entrega cada mensagem no momento exato em que chega, direto no seu servidor.
Neste guia você aprende a configurar webhooks com a ZAP-API, processar eventos em Node.js, Python e PHP, e implementar boas práticas de produção como fila de retentativa e idempotência.
O que é um webhook WhatsApp?
Um webhook é uma URL do seu servidor que a ZAP-API chama automaticamente toda vez que um evento acontece na sua instância WhatsApp. Os eventos incluem:
- message.received — nova mensagem recebida
- message.sent — mensagem enviada com sucesso
- message.failed — falha na entrega
- instance.connected — instância conectou ao WhatsApp
- instance.disconnected — instância desconectou
Passo 1: Configurar o webhook na ZAP-API
Com sua instância criada, configure a URL do webhook com uma chamada PUT:
curl -X PUT https://api.zap-api.tech/v1/instances/SEU_INSTANCE_ID/webhook \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://seu-servidor.com/webhook/whatsapp",
"events": ["message.received", "message.sent", "message.failed"]
}'
A URL precisa ser publicamente acessível (HTTPS em produção). Para desenvolvimento local, use ngrok ou localtunnel:
npx localtunnel --port 3000
# Saída: https://abc123.loca.lt — use essa URL no webhook
Passo 2: Receber eventos em Node.js
Crie um endpoint Express que processa os eventos recebidos:
import express from 'express';
const app = express();
app.use(express.json());
// Payload que a ZAP API envia
interface ZapWebhookPayload {
event: string;
instanceId: string;
data: {
messageId: string;
phone: string; // ex: "5511999998888"
name: string;
body: string; // texto da mensagem
type: string; // text | image | audio | video | document
timestamp: number;
mediaUrl?: string; // URL da mídia (se houver)
};
}
app.post('/webhook/whatsapp', async (req, res) => {
// Responda 200 imediatamente — não bloqueie o webhook
res.sendStatus(200);
const payload = req.body as ZapWebhookPayload;
if (payload.event === 'message.received') {
const { phone, name, body, type } = payload.data;
console.log(`📩 Nova mensagem de ${name} (${phone}): ${body}`);
// Processe de forma assíncrona
await processIncomingMessage(payload.data);
}
});
async function processIncomingMessage(data: ZapWebhookPayload['data']) {
// Sua lógica aqui: salvar no banco, acionar chatbot, notificar equipe, etc.
console.log(`Processando mensagem ${data.messageId} de ${data.phone}`);
}
app.listen(3000, () => console.log('Webhook server rodando na porta 3000'));
Passo 3: Receber eventos em Python
from flask import Flask, request, jsonify
import threading
app = Flask(__name__)
@app.route('/webhook/whatsapp', methods=['POST'])
def webhook():
# Responda 200 imediatamente
payload = request.json
threading.Thread(target=process_event, args=(payload,)).start()
return jsonify({'ok': True}), 200
def process_event(payload):
event = payload.get('event')
data = payload.get('data', {})
if event == 'message.received':
phone = data.get('phone')
body = data.get('body')
print(f'📩 Mensagem de {phone}: {body}')
# Sua lógica aqui
if __name__ == '__main__':
app.run(port=3000)
Passo 4: Receber eventos em PHP
<?php
// webhook.php
$payload = json_decode(file_get_contents('php://input'), true);
$event = $payload['event'] ?? '';
$data = $payload['data'] ?? [];
// Responda 200 imediatamente
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['ok' => true]);
// Processe em background (fastcgi_finish_request libera a conexão)
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
if ($event === 'message.received') {
$phone = $data['phone'] ?? '';
$body = $data['body'] ?? '';
error_log("Mensagem de $phone: $body");
// Sua lógica aqui
}
Boas práticas de produção
1. Responda 200 imediatamente
A ZAP-API aguarda confirmação do seu servidor. Se demorar mais de 5 segundos, tentará reenviar. Sempre responda 200 antes de processar e processe de forma assíncrona (fila, thread, processo separado).
2. Implemente idempotência
Use o messageId como chave de deduplicação no banco de dados. Webhooks podem ser entregues mais de uma vez em caso de falha na rede:
// Antes de processar, verifique se já processou
const exists = await db.processedMessages.findOne({ messageId: data.messageId });
if (exists) return; // Já processado — ignorar
await db.processedMessages.create({ messageId: data.messageId });
// ... processar mensagem
3. Use fila para workloads pesados
Para chatbots com IA, integrações com CRM ou envio de mídia, use uma fila (Bull, BullMQ, Redis Streams) para não bloquear o webhook:
import { Queue } from 'bullmq';
const messageQueue = new Queue('whatsapp-messages', { connection: redis });
app.post('/webhook/whatsapp', async (req, res) => {
res.sendStatus(200); // Responde imediatamente
await messageQueue.add('process', req.body, { attempts: 3, backoff: 1000 });
});
4. Valide a origem do webhook
Configure um secret no cabeçalho e valide-o para garantir que a requisição veio da ZAP-API:
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
app.post('/webhook/whatsapp', (req, res) => {
const secret = req.headers['x-webhook-secret'];
if (secret !== WEBHOOK_SECRET) return res.sendStatus(401);
// ... processar
});
Testando o webhook localmente
Use o ngrok para expor seu servidor local:
# Instalar ngrok: https://ngrok.com
ngrok http 3000
# Saída:
# Forwarding https://abc123.ngrok.io -> http://localhost:3000
# Configure o webhook com a URL do ngrok:
curl -X PUT https://api.zap-api.tech/v1/instances/SEU_INSTANCE/webhook \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://abc123.ngrok.io/webhook/whatsapp"}'
Estrutura do payload completo
{
"event": "message.received",
"instanceId": "inst_abc123",
"data": {
"messageId": "msg_xyz789",
"phone": "5511999998888",
"name": "João Silva",
"body": "Olá, preciso de ajuda",
"type": "text",
"timestamp": 1709654400,
"pushName": "João",
"isGroup": false,
"mediaUrl": null
}
}
Para mensagens de imagem, áudio ou vídeo, o campo mediaUrl conterá a URL temporária da mídia (válida por 24h).
FAQ
Preciso de HTTPS para o webhook?
Em produção sim — a ZAP-API só envia para URLs HTTPS. Em desenvolvimento, use ngrok ou localtunnel que já fornecem HTTPS gratuitamente.
O que acontece se meu servidor estiver offline?
A ZAP-API faz até 6 tentativas: a primeira imediata e as seguintes depois de 1min, 5min, 15min, 1h, 6h — cerca de 7 horas no total. Entregas que esgotam as tentativas ficam guardadas por 90 dias e podem ser reenviadas pela API (POST /v1/instances/:id/webhook/deliveries/:deliveryId/replay) ou pelo painel.
Posso ter múltiplos webhooks?
Cada instância aceita uma URL de webhook. Para fanout (enviar para múltiplos endpoints), implemente esse roteamento no seu próprio servidor.
Como debugar webhooks?
Use o webhook.site para inspecionar payloads antes de implementar seu servidor, ou ative logs detalhados no ngrok com ngrok http 3000 --inspect.