Webhooks
This content is not available in your language yet.
Webhooks permitem que seu servidor receba notificações em tempo real sempre que um evento ocorre na sessão WhatsApp. Quando configurado, a wiApi envia um HTTP POST com o payload do evento para a URL registrada. Cada request inclui uma assinatura HMAC para verificação de autenticidade.
Criar Webhook
Seção intitulada “Criar Webhook”POST https://api.wi.api.br/webhooks| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
sessionId | string | Sim | ID da sessão que emitirá os eventos |
url | string | Sim | URL do seu servidor que receberá os POSTs |
secret | string | Não | Chave para assinatura HMAC-SHA256 |
events | string[] | Sim | Lista de eventos para escutar |
retryCount | number | Não | Número de tentativas em caso de falha (padrão: 5) |
curl -X POST https://api.wi.api.br/webhooks \ -H "x-api-key: wapi_k8s2m9d4f7a1b3c6e0g5h" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "url": "https://meu-servidor.com/webhooks/whatsapp", "secret": "whsec_r7k2m9x4p1n8v3q6w0j5", "events": ["message", "connected", "disconnected", "qr"], "retryCount": 5 }'const response = await fetch("https://api.wi.api.br/webhooks", { method: "POST", headers: { "x-api-key": "wapi_k8s2m9d4f7a1b3c6e0g5h", "Content-Type": "application/json", }, body: JSON.stringify({ sessionId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", url: "https://meu-servidor.com/webhooks/whatsapp", secret: "whsec_r7k2m9x4p1n8v3q6w0j5", events: ["message", "connected", "disconnected", "qr"], retryCount: 5, }),});import requests
response = requests.post( "https://api.wi.api.br/webhooks", headers={ "x-api-key": "wapi_k8s2m9d4f7a1b3c6e0g5h", "Content-Type": "application/json", }, json={ "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "url": "https://meu-servidor.com/webhooks/whatsapp", "secret": "whsec_r7k2m9x4p1n8v3q6w0j5", "events": ["message", "connected", "disconnected", "qr"], "retryCount": 5, },)Resposta 200:
{ "id": "wh_9f8e7d6c5b4a3210", "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "url": "https://meu-servidor.com/webhooks/whatsapp", "events": ["message", "connected", "disconnected", "qr"], "retryCount": 5, "createdAt": "2026-06-01T14:23:00.000Z"}Atualizar Webhook
Seção intitulada “Atualizar Webhook”PATCH https://api.wi.api.br/webhooks/{id}| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
url | string | Não | Nova URL de destino |
secret | string | Não | Nova chave HMAC |
events | string[] | Não | Nova lista de eventos |
retryCount | number | Não | Novo número de tentativas |
curl -X PATCH https://api.wi.api.br/webhooks/wh_9f8e7d6c5b4a3210 \ -H "x-api-key: wapi_k8s2m9d4f7a1b3c6e0g5h" \ -H "Content-Type: application/json" \ -d '{ "events": ["message", "connected", "disconnected", "qr", "message_ack"], "retryCount": 3 }'const response = await fetch("https://api.wi.api.br/webhooks/wh_9f8e7d6c5b4a3210", { method: "PATCH", headers: { "x-api-key": "wapi_k8s2m9d4f7a1b3c6e0g5h", "Content-Type": "application/json", }, body: JSON.stringify({ events: ["message", "connected", "disconnected", "qr", "message_ack"], retryCount: 3, }),});Resposta 200:
{ "id": "wh_9f8e7d6c5b4a3210", "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "url": "https://meu-servidor.com/webhooks/whatsapp", "events": ["message", "connected", "disconnected", "qr", "message_ack"], "retryCount": 3, "createdAt": "2026-06-01T14:23:00.000Z", "updatedAt": "2026-06-02T09:15:00.000Z"}Deletar Webhook
Seção intitulada “Deletar Webhook”DELETE https://api.wi.api.br/webhooks/{id}curl -X DELETE https://api.wi.api.br/webhooks/wh_9f8e7d6c5b4a3210 \ -H "x-api-key: wapi_k8s2m9d4f7a1b3c6e0g5h"const response = await fetch("https://api.wi.api.br/webhooks/wh_9f8e7d6c5b4a3210", { method: "DELETE", headers: { "x-api-key": "wapi_k8s2m9d4f7a1b3c6e0g5h" },});Resposta 200:
{ "deleted": true}Eventos
Seção intitulada “Eventos”| Evento | Alias | Descrição |
|---|---|---|
message | messages | Nova mensagem recebida (texto, mídia, localização, contato) |
message_ack | Confirmação de leitura ou entrega de mensagem enviada | |
connected | connection | Sessão conectada ao WhatsApp |
disconnected | Sessão desconectada (logout, ban, conflito) | |
qr | Novo QR code gerado para pareamento | |
group_join | Participante entrou em um grupo | |
group_leave | Participante saiu de um grupo | |
call | Chamada recebida (voz ou vídeo) | |
presence | Atualização de status online/offline de contato | |
chat_archive | Chat arquivado ou desarquivado |
Aliases existem para compatibilidade com integrações que esperam o nome no plural (Chatwoot, Mega).
Formato do Payload
Seção intitulada “Formato do Payload”Cada evento é entregue como POST JSON na URL registrada:
{ "event": "message", "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "data": { "id": "3EB0A6C2F8B1D4E9F7A2", "chat": "5511998765432@s.whatsapp.net", "chatId": "5511998765432@s.whatsapp.net", "remoteJid": "5511998765432@s.whatsapp.net", "sender": "5511912345678@s.whatsapp.net", "pushName": "João Silva", "text": "Boa tarde, preciso do orçamento atualizado.", "timestamp": 1748790000, "fromMe": false, "type": "text", "mediaUrl": null }, "timestamp": "2026-06-01T15:00:00.000Z"}Headers incluídos em cada request:
| Header | Valor |
|---|---|
Content-Type | application/json |
x-webhook-signature | HMAC-SHA256 hex do body (se secret configurado) |
x-webhook-id | ID único do dispatch (para deduplicação) |
x-webhook-timestamp | Timestamp ISO 8601 do envio |
Verificação HMAC
Seção intitulada “Verificação HMAC”Se o campo secret estiver configurado no webhook, cada request inclui o header x-webhook-signature com o HMAC-SHA256 do body. Valide para garantir que o payload veio da wiApi e não foi adulterado.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhook(body, signature, secret) { const expected = createHmac("sha256", secret) .update(body, "utf-8") .digest("hex");
return timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expected, "hex") );}
// Uso no handler (Express/Fastify/etc.)app.post("/webhooks/whatsapp", (req, res) => { const signature = req.headers["x-webhook-signature"]; const rawBody = req.rawBody; // buffer do body original
if (!verifyWebhook(rawBody, signature, "whsec_r7k2m9x4p1n8v3q6w0j5")) { return res.status(401).send("Invalid signature"); }
const event = JSON.parse(rawBody); // processar evento... res.status(200).send("OK");});import hmacimport hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode("utf-8"), body, hashlib.sha256, ).hexdigest()
return hmac.compare_digest(expected, signature)
# Uso no handler (Flask/FastAPI/etc.)@app.post("/webhooks/whatsapp")def handle_webhook(request): signature = request.headers.get("x-webhook-signature") body = request.get_data()
if not verify_webhook(body, signature, "whsec_r7k2m9x4p1n8v3q6w0j5"): return "Invalid signature", 401
event = request.get_json() # processar evento... return "OK", 200Se o endpoint retornar status HTTP ≥ 400 ou não responder em 10 segundos, a wiApi faz retry com exponential backoff:
| Tentativa | Delay após falha | Tempo acumulado |
|---|---|---|
| 1ª | imediata | 0s |
| 2ª | 1s | 1s |
| 3ª | 4s | 5s |
| 4ª | 16s | 21s |
| 5ª | 64s | 85s |
Após esgotar todas as tentativas, o evento é movido para a Dead Letter Queue (DLQ). Eventos na DLQ ficam disponíveis para inspeção e reenvio manual via dashboard por 7 dias.
Retorne 200 o mais rápido possível. Processe o evento de forma assíncrona para evitar timeouts.