> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wappfy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receba notificações em tempo real para mensagens, eventos de entrega e mudanças de status de instância.

# 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:

<AccordionGroup>
  <Accordion title="Eventos de Mensagem">
    | Evento              | Descrição                                                                       |
    | ------------------- | ------------------------------------------------------------------------------- |
    | `message.received`  | Uma nova mensagem de entrada foi recebida.                                      |
    | `message.sent`      | Uma mensagem de saída foi enviada com sucesso.                                  |
    | `message.delivered` | Uma mensagem enviada foi entregue no dispositivo do destinatário (duplo check). |
    | `message.read`      | Uma mensagem enviada foi lida pelo destinatário (checks azuis).                 |
    | `message.failed`    | Uma mensagem de saída falhou no envio.                                          |
    | `message.reaction`  | Alguém reagiu a uma mensagem com um emoji.                                      |
  </Accordion>

  <Accordion title="Eventos de Instância">
    | Evento                  | Descrição                                          |
    | ----------------------- | -------------------------------------------------- |
    | `instance.connected`    | Uma instância se conectou ao WhatsApp com sucesso. |
    | `instance.disconnected` | Uma instância perdeu a conexão com o WhatsApp.     |
    | `instance.qr`           | Um novo QR code está disponível para escaneamento. |
  </Accordion>

  <Accordion title="Eventos de Grupo e Contato">
    | Evento            | Descrição                                                     |
    | ----------------- | ------------------------------------------------------------- |
    | `group.joined`    | Um participante entrou em um grupo (incluindo o próprio bot). |
    | `group.left`      | Um participante saiu de um grupo.                             |
    | `contact.created` | Um novo contato foi salvo ou detectado.                       |
  </Accordion>
</AccordionGroup>

***

## Criar um Webhook

Registre um endpoint de webhook para começar a receber eventos.

<CodeGroup>
  ```bash cURL theme={null}
  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
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.wappfy.io/api/webhooks", {
    method: "POST",
    headers: {
      "X-Api-Key": "SUA_CHAVE_API",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      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,
    }),
  });

  const webhook = await response.json();
  console.log(webhook.data.id);
  ```
</CodeGroup>

**Resposta:**

```json theme={null}
{
  "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

| Campo         | Tipo      | Padrão          | Descrição                                                                                    |
| ------------- | --------- | --------------- | -------------------------------------------------------------------------------------------- |
| `url`         | string    | **obrigatório** | A URL HTTPS que receberá as requisições POST do webhook.                                     |
| `events`      | string\[] | **obrigatório** | Array de tipos de evento para assinar.                                                       |
| `instance_id` | string    | `null`          | Limite o webhook a uma instância específica. Se nulo, recebe eventos de todas as instâncias. |
| `secret`      | string    | `null`          | Segredo usado para gerar assinaturas HMAC para verificação do payload.                       |
| `retry_count` | number    | `3`             | Número de tentativas de reenvio em caso de falha (0-5).                                      |
| `timeout_ms`  | number    | `10000`         | Timeout da requisição em milissegundos (1000-30000).                                         |

<Note>
  O campo `instance_id` é opcional. Se omitido, o webhook receberá eventos de **todas** as instâncias da sua conta.
</Note>

***

## Listar Webhooks

```bash theme={null}
curl https://api.wappfy.io/api/webhooks \
  -H "X-Api-Key: SUA_CHAVE_API"
```

**Resposta:**

```json theme={null}
{
  "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.

```bash theme={null}
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

```bash theme={null}
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:

```json theme={null}
{
  "id": "dlv_abc123def456",
  "event": "message.received",
  "instance_id": "inst_abc123",
  "timestamp": "2026-02-10T14:30:00Z",
  "data": {
    "message_id": "BAE5F2C4D3B2A1",
    "chat_id": "5511999998888@s.whatsapp.net",
    "from": "5511999998888@s.whatsapp.net",
    "type": "text",
    "text": "Olá!",
    "timestamp": "2026-02-10T14:30:00Z"
  }
}
```

### Campos do Payload

| Campo         | Descrição                                                          |
| ------------- | ------------------------------------------------------------------ |
| `id`          | ID único de entrega. Use para deduplicação.                        |
| `event`       | O tipo de evento que disparou esta entrega.                        |
| `instance_id` | A instância que gerou o evento.                                    |
| `timestamp`   | Timestamp ISO 8601 de quando o evento ocorreu.                     |
| `data`        | Payload 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

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  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 });
  });
  ```

  ```python Python (Flask) theme={null}
  import hmac
  import hashlib
  import json
  from flask import Flask, request, jsonify

  app = Flask(__name__)
  WEBHOOK_SECRET = "whsec_meu_segredo_assinatura"

  def verificar_assinatura(payload, signature):
      expected = hmac.new(
          WEBHOOK_SECRET.encode(),
          json.dumps(payload).encode(),
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(expected, signature)

  @app.route("/webhooks/wappfy", methods=["POST"])
  def handle_webhook():
      signature = request.headers.get("X-Wappfy-Signature", "")
      if not verificar_assinatura(request.json, signature):
          return jsonify({"error": "Assinatura inválida"}), 401

      event = request.json["event"]
      data = request.json["data"]
      print(f"Evento recebido: {event}", data)

      return jsonify({"received": True}), 200
  ```
</CodeGroup>

<Warning>
  Sempre use comparação em tempo constante (como `timingSafeEqual` ou `hmac.compare_digest`) ao verificar assinaturas para prevenir ataques de temporização.
</Warning>

***

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

| Tentativa      | Atraso      |
| -------------- | ----------- |
| 1a retentativa | 10 segundos |
| 2a retentativa | 60 segundos |
| 3a retentativa | 5 minutos   |
| 4a retentativa | 30 minutos  |
| 5a retentativa | 2 horas     |

<Note>
  As retentativas param quando uma resposta `2xx` é recebida ou o `retry_count` é esgotado. O número padrão de retentativas é 3.
</Note>

### Visualizando o Histórico de Entregas

Consulte o log de entregas de um webhook para ver tentativas passadas e seus resultados.

```bash theme={null}
curl https://api.wappfy.io/api/webhooks/wh_xyz789/deliveries \
  -H "X-Api-Key: SUA_CHAVE_API"
```

**Resposta:**

```json theme={null}
{
  "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

<AccordionGroup>
  <Accordion title="message.received">
    ```json theme={null}
    {
      "id": "dlv_abc123",
      "event": "message.received",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:30:00Z",
      "data": {
        "message_id": "BAE5F2C4D3B2A1",
        "chat_id": "5511999998888@s.whatsapp.net",
        "from": "5511999998888@s.whatsapp.net",
        "type": "text",
        "text": "Olá, preciso de ajuda com meu pedido",
        "timestamp": "2026-02-10T14:30:00Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="message.delivered">
    ```json theme={null}
    {
      "id": "dlv_def456",
      "event": "message.delivered",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:30:05Z",
      "data": {
        "message_id": "BAE5A1B2C3D4E5",
        "chat_id": "5511999998888@s.whatsapp.net",
        "status": "delivered"
      }
    }
    ```
  </Accordion>

  <Accordion title="message.read">
    ```json theme={null}
    {
      "id": "dlv_ghi789",
      "event": "message.read",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:31:00Z",
      "data": {
        "message_id": "BAE5A1B2C3D4E5",
        "chat_id": "5511999998888@s.whatsapp.net",
        "status": "read"
      }
    }
    ```
  </Accordion>

  <Accordion title="message.reaction">
    ```json theme={null}
    {
      "id": "dlv_jkl012",
      "event": "message.reaction",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:32:00Z",
      "data": {
        "message_id": "BAE5F2C4D3B2A1",
        "chat_id": "5511999998888@s.whatsapp.net",
        "from": "5511999998888@s.whatsapp.net",
        "reaction": "\u2764\ufe0f"
      }
    }
    ```
  </Accordion>

  <Accordion title="message.failed">
    ```json theme={null}
    {
      "id": "dlv_uvw234",
      "event": "message.failed",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:33:00Z",
      "data": {
        "message_id": "BAE5X1Y2Z3W4V5",
        "chat_id": "5511999998888@s.whatsapp.net",
        "error": "Número não encontrado no WhatsApp"
      }
    }
    ```
  </Accordion>

  <Accordion title="message.sent">
    ```json theme={null}
    {
      "id": "dlv_xyz567",
      "event": "message.sent",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:30:02Z",
      "data": {
        "message_id": "BAE5A1B2C3D4E5",
        "chat_id": "5511999998888@s.whatsapp.net",
        "type": "text",
        "text": "Sua resposta automática",
        "status": "sent"
      }
    }
    ```
  </Accordion>

  <Accordion title="instance.connected">
    ```json theme={null}
    {
      "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"
      }
    }
    ```
  </Accordion>

  <Accordion title="instance.disconnected">
    ```json theme={null}
    {
      "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"
      }
    }
    ```
  </Accordion>

  <Accordion title="instance.qr">
    ```json theme={null}
    {
      "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..."
      }
    }
    ```
  </Accordion>

  <Accordion title="group.joined">
    ```json theme={null}
    {
      "id": "dlv_stu901",
      "event": "group.joined",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T15:00:00Z",
      "data": {
        "group_id": "120363012345678901@g.us",
        "participant": "5511888887777@s.whatsapp.net"
      }
    }
    ```
  </Accordion>

  <Accordion title="group.left">
    ```json theme={null}
    {
      "id": "dlv_def234",
      "event": "group.left",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T16:00:00Z",
      "data": {
        "group_id": "120363012345678901@g.us",
        "participant": "5511888887777@s.whatsapp.net"
      }
    }
    ```
  </Accordion>

  <Accordion title="contact.created">
    ```json theme={null}
    {
      "id": "dlv_ghi567",
      "event": "contact.created",
      "instance_id": "inst_abc123",
      "timestamp": "2026-02-10T14:35:00Z",
      "data": {
        "contact_id": "5511777776666@s.whatsapp.net",
        "phone": "5511777776666",
        "name": "Novo Contato"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Boas Práticas

<CardGroup cols={2}>
  <Card title="Responda rapidamente" icon="bolt">
    Retorne um status `200` em até 5 segundos. Processe o evento de forma assíncrona para evitar timeouts.
  </Card>

  <Card title="Deduplique" icon="clone">
    Use o `id` da entrega para detectar e ignorar entregas duplicadas causadas por retentativas.
  </Card>

  <Card title="Verifique assinaturas" icon="shield-halved">
    Sempre valide o header `X-Wappfy-Signature` se você configurou um segredo.
  </Card>

  <Card title="Use HTTPS" icon="lock">
    URLs de webhook devem usar HTTPS. Endpoints HTTP serão rejeitados.
  </Card>
</CardGroup>
