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

# Webhook

> Ricevi notifiche in tempo reale per messaggi, eventi di consegna e cambiamenti di stato delle istanze.

# Webhook

I webhook ti permettono di ricevere callback HTTP in tempo reale quando si verificano eventi sulle tue istanze WhatsApp. Invece di interrogare continuamente l'API, Wappfy invia gli eventi al tuo server nel momento in cui si verificano.

## Eventi supportati

Wappfy supporta 12 tipi di eventi webhook:

<AccordionGroup>
  <Accordion title="Eventi dei messaggi">
    | Evento              | Descrizione                                                                              |
    | ------------------- | ---------------------------------------------------------------------------------------- |
    | `message.received`  | Un nuovo messaggio in arrivo e stato ricevuto.                                           |
    | `message.sent`      | Un messaggio in uscita e stato inviato con successo.                                     |
    | `message.delivered` | Un messaggio inviato e stato consegnato al dispositivo del destinatario (doppia spunta). |
    | `message.read`      | Un messaggio inviato e stato letto dal destinatario (spunte blu).                        |
    | `message.failed`    | L'invio di un messaggio in uscita e fallito.                                             |
    | `message.reaction`  | Qualcuno ha reagito a un messaggio con un emoji.                                         |
  </Accordion>

  <Accordion title="Eventi delle istanze">
    | Evento                  | Descrizione                                        |
    | ----------------------- | -------------------------------------------------- |
    | `instance.connected`    | Un'istanza si e connessa con successo a WhatsApp.  |
    | `instance.disconnected` | Un'istanza ha perso la connessione a WhatsApp.     |
    | `instance.qr`           | Un nuovo codice QR e disponibile per la scansione. |
  </Accordion>

  <Accordion title="Eventi di gruppi e contatti">
    | Evento            | Descrizione                                                     |
    | ----------------- | --------------------------------------------------------------- |
    | `group.joined`    | Un partecipante si e unito a un gruppo (incluso il bot stesso). |
    | `group.left`      | Un partecipante ha lasciato un gruppo.                          |
    | `contact.created` | Un nuovo contatto e stato salvato o rilevato.                   |
  </Accordion>
</AccordionGroup>

***

## Creare un webhook

Registra un endpoint webhook per iniziare a ricevere eventi.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.wappfy.io/api/webhooks \
    -H "X-Api-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://your-server.com/webhooks/wappfy",
      "events": ["message.received", "message.sent", "message.delivered"],
      "instance_id": "inst_abc123",
      "secret": "whsec_my_signing_secret",
      "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": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://your-server.com/webhooks/wappfy",
      events: ["message.received", "message.sent", "message.delivered"],
      instance_id: "inst_abc123",
      secret: "whsec_my_signing_secret",
      retry_count: 3,
      timeout_ms: 10000,
    }),
  });

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

**Risposta:**

```json theme={null}
{
  "data": {
    "id": "wh_xyz789",
    "url": "https://your-server.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"
  }
}
```

### Opzioni di configurazione

| Campo         | Tipo      | Predefinito      | Descrizione                                                                           |
| ------------- | --------- | ---------------- | ------------------------------------------------------------------------------------- |
| `url`         | string    | **obbligatorio** | L'URL HTTPS che ricevera le richieste POST del webhook.                               |
| `events`      | string\[] | **obbligatorio** | Array dei tipi di evento a cui iscriversi.                                            |
| `instance_id` | string    | `null`           | Limita il webhook a un'istanza specifica. Se null, riceve eventi da tutte le istanze. |
| `secret`      | string    | `null`           | Segreto utilizzato per generare firme HMAC per la verifica del payload.               |
| `retry_count` | number    | `3`              | Numero di tentativi in caso di errore nella consegna (0-5).                           |
| `timeout_ms`  | number    | `10000`          | Timeout della richiesta in millisecondi (1000-30000).                                 |

<Note>
  Il campo `instance_id` e opzionale. Se omesso, il webhook ricevera eventi da **tutte** le istanze del tuo account.
</Note>

***

## Elencare i webhook

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

**Risposta:**

```json theme={null}
{
  "data": [
    {
      "id": "wh_xyz789",
      "url": "https://your-server.com/webhooks/wappfy",
      "events": ["message.received", "message.sent", "message.delivered"],
      "instance_id": "inst_abc123",
      "is_active": true,
      "retry_count": 3,
      "timeout_ms": 10000
    }
  ]
}
```

***

## Aggiornare un webhook

Aggiorna l'URL, gli eventi o la configurazione di un webhook esistente.

```bash theme={null}
curl -X PATCH https://api.wappfy.io/api/webhooks/wh_xyz789 \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["message.received", "message.sent", "message.delivered", "message.read"],
    "is_active": true
  }'
```

***

## Eliminare un webhook

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

***

## Formato del payload di consegna

Quando si verifica un evento, Wappfy invia una richiesta POST all'URL del tuo webhook con la seguente struttura:

```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": "Hello!",
    "timestamp": "2026-02-10T14:30:00Z"
  }
}
```

### Campi del payload

| Campo         | Descrizione                                                                  |
| ------------- | ---------------------------------------------------------------------------- |
| `id`          | ID univoco della consegna. Usalo per la deduplicazione.                      |
| `event`       | Il tipo di evento che ha attivato questa consegna.                           |
| `instance_id` | L'istanza che ha generato l'evento.                                          |
| `timestamp`   | Timestamp ISO 8601 del momento in cui si e verificato l'evento.              |
| `data`        | Payload specifico dell'evento. Il contenuto varia in base al tipo di evento. |

***

## Verifica della firma HMAC

Se fornisci un `secret` durante la creazione del webhook, ogni consegna includera un header `X-Wappfy-Signature` contenente una firma HMAC-SHA256 del corpo della richiesta.

**Verifica sempre questa firma** per assicurarti che la richiesta provenga da Wappfy e non sia stata alterata.

### Esempi di verifica

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  const crypto = require("crypto");

  function verifyWebhookSignature(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)
    );
  }

  // Express middleware
  app.post("/webhooks/wappfy", (req, res) => {
    const isValid = verifyWebhookSignature(req, "whsec_my_signing_secret");

    if (!isValid) {
      return res.status(401).json({ error: "Invalid signature" });
    }

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

    // Always respond with 200 quickly to prevent retries
    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_my_signing_secret"

  def verify_signature(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 verify_signature(request.json, signature):
          return jsonify({"error": "Invalid signature"}), 401

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

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

<Warning>
  Usa sempre un confronto a tempo costante (come `timingSafeEqual` o `hmac.compare_digest`) durante la verifica delle firme per prevenire attacchi di timing.
</Warning>

***

## Comportamento dei tentativi

Se il tuo server non risponde con un codice di stato `2xx` entro il `timeout_ms` configurato, Wappfy ritentera la consegna.

| Tentativo    | Ritardo    |
| ------------ | ---------- |
| 1o tentativo | 10 secondi |
| 2o tentativo | 60 secondi |
| 3o tentativo | 5 minuti   |
| 4o tentativo | 30 minuti  |
| 5o tentativo | 2 ore      |

<Note>
  I tentativi si fermano quando viene ricevuta una risposta `2xx` o quando il `retry_count` e esaurito. Il numero predefinito di tentativi e 3.
</Note>

### Visualizzare lo storico delle consegne

Controlla il log delle consegne di un webhook per vedere i tentativi passati e i relativi risultati.

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

**Risposta:**

```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": "Server returned 500 Internal Server Error"
    }
  ]
}
```

***

## Esempi di payload degli eventi

<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": "Hello, I need help with my order",
        "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="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.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>
</AccordionGroup>

***

## Buone pratiche

<CardGroup cols={2}>
  <Card title="Rispondi rapidamente" icon="bolt">
    Restituisci uno stato `200` entro 5 secondi. Elabora l'evento in modo asincrono per evitare timeout.
  </Card>

  <Card title="Deduplica" icon="clone">
    Usa l'`id` della consegna per rilevare e saltare le consegne duplicate causate dai tentativi.
  </Card>

  <Card title="Verifica le firme" icon="shield-halved">
    Valida sempre l'header `X-Wappfy-Signature` se hai configurato un segreto.
  </Card>

  <Card title="Usa HTTPS" icon="lock">
    Gli URL dei webhook devono utilizzare HTTPS. Gli endpoint HTTP verranno rifiutati.
  </Card>
</CardGroup>
