Pular para o conteúdo principal

Webhooks

Webhooks permitem que você receba callbacks HTTP em tempo real quando eventos ocorrem nas suas instâncias WhatsApp. Em vez de fazer polling na API, a Wappfy envia os eventos para o seu servidor conforme eles acontecem.

Eventos Suportados

A Wappfy suporta 12 tipos de eventos de webhook:
EventoDescrição
message.receivedUma nova mensagem de entrada foi recebida.
message.sentUma mensagem de saída foi enviada com sucesso.
message.deliveredUma mensagem enviada foi entregue no dispositivo do destinatário (duplo check).
message.readUma mensagem enviada foi lida pelo destinatário (checks azuis).
message.failedUma mensagem de saída falhou no envio.
message.reactionAlguém reagiu a uma mensagem com um emoji.
EventoDescrição
instance.connectedUma instância se conectou ao WhatsApp com sucesso.
instance.disconnectedUma instância perdeu a conexão com o WhatsApp.
instance.qrUm novo QR code está disponível para escaneamento.
EventoDescrição
group.joinedUm participante entrou em um grupo (incluindo o próprio bot).
group.leftUm participante saiu de um grupo.
contact.createdUm novo contato foi salvo ou detectado.

Criar um Webhook

Registre um endpoint de webhook para começar a receber eventos.
curl -X POST https://api.wappfy.io/api/webhooks \
  -H "X-Api-Key: SUA_CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://seu-servidor.com/webhooks/wappfy",
    "events": ["message.received", "message.sent", "message.delivered"],
    "instance_id": "inst_abc123",
    "secret": "whsec_meu_segredo_assinatura",
    "retry_count": 3,
    "timeout_ms": 10000
  }'
Resposta:
{
  "data": {
    "id": "wh_xyz789",
    "url": "https://seu-servidor.com/webhooks/wappfy",
    "events": ["message.received", "message.sent", "message.delivered"],
    "instance_id": "inst_abc123",
    "is_active": true,
    "retry_count": 3,
    "timeout_ms": 10000,
    "created_at": "2026-02-10T12:00:00Z"
  }
}

Opções de Configuração

CampoTipoPadrãoDescrição
urlstringobrigatórioA URL HTTPS que receberá as requisições POST do webhook.
eventsstring[]obrigatórioArray de tipos de evento para assinar.
instance_idstringnullLimite o webhook a uma instância específica. Se nulo, recebe eventos de todas as instâncias.
secretstringnullSegredo usado para gerar assinaturas HMAC para verificação do payload.
retry_countnumber3Número de tentativas de reenvio em caso de falha (0-5).
timeout_msnumber10000Timeout da requisição em milissegundos (1000-30000).
O campo instance_id é opcional. Se omitido, o webhook receberá eventos de todas as instâncias da sua conta.

Listar Webhooks

curl https://api.wappfy.io/api/webhooks \
  -H "X-Api-Key: SUA_CHAVE_API"
Resposta:
{
  "data": [
    {
      "id": "wh_xyz789",
      "url": "https://seu-servidor.com/webhooks/wappfy",
      "events": ["message.received", "message.sent", "message.delivered"],
      "instance_id": "inst_abc123",
      "is_active": true,
      "retry_count": 3,
      "timeout_ms": 10000
    }
  ]
}

Atualizar um Webhook

Atualize a URL, eventos ou configuração de um webhook existente.
curl -X PATCH https://api.wappfy.io/api/webhooks/wh_xyz789 \
  -H "X-Api-Key: SUA_CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["message.received", "message.sent", "message.delivered", "message.read"],
    "is_active": true
  }'

Excluir um Webhook

curl -X DELETE https://api.wappfy.io/api/webhooks/wh_xyz789 \
  -H "X-Api-Key: SUA_CHAVE_API"

Formato do Payload de Entrega

Quando um evento ocorre, a Wappfy envia uma requisição POST para a URL do seu webhook com a seguinte estrutura:
{
  "id": "dlv_abc123def456",
  "event": "message.received",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:30:00Z",
  "data": {
    "message_id": "BAE5F2C4D3B2A1",
    "chat_id": "[email protected]",
    "from": "[email protected]",
    "type": "text",
    "text": "Olá!",
    "timestamp": "2026-02-10T14:30:00Z"
  }
}

Campos do Payload

CampoDescrição
idID único de entrega. Use para deduplicação.
eventO tipo de evento que disparou esta entrega.
instance_idA instância que gerou o evento.
timestampTimestamp ISO 8601 de quando o evento ocorreu.
dataPayload específico do evento. O conteúdo varia por tipo de evento.

Verificação de Assinatura HMAC

Se você fornecer um secret ao criar um webhook, cada entrega incluirá um header X-Wappfy-Signature contendo uma assinatura HMAC-SHA256 do corpo da requisição. Sempre verifique esta assinatura para garantir que a requisição veio da Wappfy e não foi adulterada.

Exemplos de Verificação

const crypto = require("crypto");

function verificarAssinaturaWebhook(req, secret) {
  const signature = req.headers["x-wappfy-signature"];
  if (!signature) return false;

  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(JSON.stringify(req.body))
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Middleware Express
app.post("/webhooks/wappfy", (req, res) => {
  const isValid = verificarAssinaturaWebhook(req, "whsec_meu_segredo_assinatura");

  if (!isValid) {
    return res.status(401).json({ error: "Assinatura inválida" });
  }

  const { event, data } = req.body;
  console.log(`Evento recebido: ${event}`, data);

  // Sempre responda com 200 rapidamente para evitar retentativas
  res.status(200).json({ received: true });
});
Sempre use comparação em tempo constante (como timingSafeEqual ou hmac.compare_digest) ao verificar assinaturas para prevenir ataques de temporização.

Comportamento de Retentativas

Se o seu servidor não responder com um código de status 2xx dentro do timeout_ms configurado, a Wappfy tentará reenviar a entrega.
TentativaAtraso
1a retentativa10 segundos
2a retentativa60 segundos
3a retentativa5 minutos
4a retentativa30 minutos
5a retentativa2 horas
As retentativas param quando uma resposta 2xx é recebida ou o retry_count é esgotado. O número padrão de retentativas é 3.

Visualizando o Histórico de Entregas

Consulte o log de entregas de um webhook para ver tentativas passadas e seus resultados.
curl https://api.wappfy.io/api/webhooks/wh_xyz789/deliveries \
  -H "X-Api-Key: SUA_CHAVE_API"
Resposta:
{
  "data": [
    {
      "id": "dlv_abc123def456",
      "event": "message.received",
      "status": "delivered",
      "http_status": 200,
      "attempts": 1,
      "created_at": "2026-02-10T14:30:00Z",
      "delivered_at": "2026-02-10T14:30:01Z"
    },
    {
      "id": "dlv_ghi789jkl012",
      "event": "message.sent",
      "status": "failed",
      "http_status": 500,
      "attempts": 3,
      "created_at": "2026-02-10T14:31:00Z",
      "last_error": "Servidor retornou 500 Internal Server Error"
    }
  ]
}

Exemplos de Payload por Evento

{
  "id": "dlv_abc123",
  "event": "message.received",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:30:00Z",
  "data": {
    "message_id": "BAE5F2C4D3B2A1",
    "chat_id": "[email protected]",
    "from": "[email protected]",
    "type": "text",
    "text": "Olá, preciso de ajuda com meu pedido",
    "timestamp": "2026-02-10T14:30:00Z"
  }
}
{
  "id": "dlv_def456",
  "event": "message.delivered",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:30:05Z",
  "data": {
    "message_id": "BAE5A1B2C3D4E5",
    "chat_id": "[email protected]",
    "status": "delivered"
  }
}
{
  "id": "dlv_ghi789",
  "event": "message.read",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:31:00Z",
  "data": {
    "message_id": "BAE5A1B2C3D4E5",
    "chat_id": "[email protected]",
    "status": "read"
  }
}
{
  "id": "dlv_jkl012",
  "event": "message.reaction",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:32:00Z",
  "data": {
    "message_id": "BAE5F2C4D3B2A1",
    "chat_id": "[email protected]",
    "from": "[email protected]",
    "reaction": "\u2764\ufe0f"
  }
}
{
  "id": "dlv_uvw234",
  "event": "message.failed",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:33:00Z",
  "data": {
    "message_id": "BAE5X1Y2Z3W4V5",
    "chat_id": "[email protected]",
    "error": "Número não encontrado no WhatsApp"
  }
}
{
  "id": "dlv_xyz567",
  "event": "message.sent",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:30:02Z",
  "data": {
    "message_id": "BAE5A1B2C3D4E5",
    "chat_id": "[email protected]",
    "type": "text",
    "text": "Sua resposta automática",
    "status": "sent"
  }
}
{
  "id": "dlv_mno345",
  "event": "instance.connected",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T12:00:00Z",
  "data": {
    "instance_id": "inst_abc123",
    "status": "connected",
    "phone_number": "5511999998888"
  }
}
{
  "id": "dlv_abc890",
  "event": "instance.disconnected",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T18:00:00Z",
  "data": {
    "instance_id": "inst_abc123",
    "status": "disconnected",
    "reason": "Sessão encerrada pelo dispositivo principal"
  }
}
{
  "id": "dlv_pqr678",
  "event": "instance.qr",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T11:59:00Z",
  "data": {
    "instance_id": "inst_abc123",
    "qr": "data:image/png;base64,iVBORw0KGgo..."
  }
}
{
  "id": "dlv_stu901",
  "event": "group.joined",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T15:00:00Z",
  "data": {
    "group_id": "[email protected]",
    "participant": "[email protected]"
  }
}
{
  "id": "dlv_def234",
  "event": "group.left",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T16:00:00Z",
  "data": {
    "group_id": "[email protected]",
    "participant": "[email protected]"
  }
}
{
  "id": "dlv_ghi567",
  "event": "contact.created",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:35:00Z",
  "data": {
    "contact_id": "[email protected]",
    "phone": "5511777776666",
    "name": "Novo Contato"
  }
}

Boas Práticas

Responda rapidamente

Retorne um status 200 em até 5 segundos. Processe o evento de forma assíncrona para evitar timeouts.

Deduplique

Use o id da entrega para detectar e ignorar entregas duplicadas causadas por retentativas.

Verifique assinaturas

Sempre valide o header X-Wappfy-Signature se você configurou um segredo.

Use HTTPS

URLs de webhook devem usar HTTPS. Endpoints HTTP serão rejeitados.