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

> Empfangen Sie Echtzeit-Benachrichtigungen fuer Nachrichten, Zustellereignisse und Instanz-Statusaenderungen.

# Webhooks

Webhooks ermoeglichen es Ihnen, HTTP-Callbacks in Echtzeit zu empfangen, wenn Ereignisse auf Ihren WhatsApp-Instanzen auftreten. Anstatt die API abzufragen, sendet Wappfy Ereignisse an Ihren Server, sobald sie eintreten.

## Unterstuetzte Ereignisse

Wappfy unterstuetzt 12 Webhook-Ereignistypen:

<AccordionGroup>
  <Accordion title="Nachrichtenereignisse">
    | Ereignis            | Beschreibung                                                                                  |
    | ------------------- | --------------------------------------------------------------------------------------------- |
    | `message.received`  | Eine neue eingehende Nachricht wurde empfangen.                                               |
    | `message.sent`      | Eine ausgehende Nachricht wurde erfolgreich gesendet.                                         |
    | `message.delivered` | Eine gesendete Nachricht wurde an das Geraet des Empfaengers zugestellt (doppeltes Haekchen). |
    | `message.read`      | Eine gesendete Nachricht wurde vom Empfaenger gelesen (blaue Haekchen).                       |
    | `message.failed`    | Eine ausgehende Nachricht konnte nicht gesendet werden.                                       |
    | `message.reaction`  | Jemand hat auf eine Nachricht mit einem Emoji reagiert.                                       |
  </Accordion>

  <Accordion title="Instanz-Ereignisse">
    | Ereignis                | Beschreibung                                              |
    | ----------------------- | --------------------------------------------------------- |
    | `instance.connected`    | Eine Instanz hat sich erfolgreich mit WhatsApp verbunden. |
    | `instance.disconnected` | Eine Instanz hat die WhatsApp-Verbindung verloren.        |
    | `instance.qr`           | Ein neuer QR-Code steht zum Scannen bereit.               |
  </Accordion>

  <Accordion title="Gruppen- & Kontakt-Ereignisse">
    | Ereignis          | Beschreibung                                                                   |
    | ----------------- | ------------------------------------------------------------------------------ |
    | `group.joined`    | Ein Teilnehmer ist einer Gruppe beigetreten (einschliesslich des Bots selbst). |
    | `group.left`      | Ein Teilnehmer hat eine Gruppe verlassen.                                      |
    | `contact.created` | Ein neuer Kontakt wurde gespeichert oder erkannt.                              |
  </Accordion>
</AccordionGroup>

***

## Webhook erstellen

Registrieren Sie einen Webhook-Endpunkt, um Ereignisse zu empfangen.

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

**Antwort:**

```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"
  }
}
```

### Konfigurationsoptionen

| Feld          | Typ       | Standard         | Beschreibung                                                                                           |
| ------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------------------ |
| `url`         | string    | **erforderlich** | Die HTTPS-URL, die Webhook-POST-Anfragen empfaengt.                                                    |
| `events`      | string\[] | **erforderlich** | Array von Ereignistypen, die abonniert werden sollen.                                                  |
| `instance_id` | string    | `null`           | Webhook auf eine bestimmte Instanz beschraenken. Bei null werden Ereignisse aller Instanzen empfangen. |
| `secret`      | string    | `null`           | Geheimnis zur Erzeugung von HMAC-Signaturen fuer die Payload-Verifizierung.                            |
| `retry_count` | number    | `3`              | Anzahl der Wiederholungsversuche bei Zustellungsfehlern (0-5).                                         |
| `timeout_ms`  | number    | `10000`          | Anfrage-Timeout in Millisekunden (1000-30000).                                                         |

<Note>
  Das Feld `instance_id` ist optional. Wenn es weggelassen wird, empfaengt der Webhook Ereignisse von **allen** Instanzen in Ihrem Konto.
</Note>

***

## Webhooks auflisten

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

**Antwort:**

```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
    }
  ]
}
```

***

## Webhook aktualisieren

Aktualisieren Sie die URL, Ereignisse oder Konfiguration eines bestehenden Webhooks.

```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
  }'
```

***

## Webhook loeschen

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

***

## Zustellungs-Payload-Format

Wenn ein Ereignis eintritt, sendet Wappfy eine POST-Anfrage an Ihre Webhook-URL mit folgender Struktur:

```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"
  }
}
```

### Payload-Felder

| Feld          | Beschreibung                                                           |
| ------------- | ---------------------------------------------------------------------- |
| `id`          | Eindeutige Zustellungs-ID. Verwenden Sie diese zur Deduplizierung.     |
| `event`       | Der Ereignistyp, der diese Zustellung ausgeloest hat.                  |
| `instance_id` | Die Instanz, die das Ereignis erzeugt hat.                             |
| `timestamp`   | ISO-8601-Zeitstempel des Ereignisses.                                  |
| `data`        | Ereignisspezifischer Payload. Der Inhalt variiert je nach Ereignistyp. |

***

## HMAC-Signaturverifizierung

Wenn Sie beim Erstellen eines Webhooks ein `secret` angeben, enthaelt jede Zustellung einen `X-Wappfy-Signature`-Header mit einer HMAC-SHA256-Signatur des Anfrageinhalts.

**Verifizieren Sie diese Signatur immer**, um sicherzustellen, dass die Anfrage von Wappfy stammt und nicht manipuliert wurde.

### Verifizierungsbeispiele

<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>
  Verwenden Sie immer einen zeitkonstanten Vergleich (wie `timingSafeEqual` oder `hmac.compare_digest`) bei der Signaturverifizierung, um Timing-Angriffe zu verhindern.
</Warning>

***

## Wiederholungsverhalten

Wenn Ihr Server nicht innerhalb der konfigurierten `timeout_ms` mit einem `2xx`-Statuscode antwortet, wiederholt Wappfy die Zustellung.

| Versuch         | Verzoegerung |
| --------------- | ------------ |
| 1. Wiederholung | 10 Sekunden  |
| 2. Wiederholung | 60 Sekunden  |
| 3. Wiederholung | 5 Minuten    |
| 4. Wiederholung | 30 Minuten   |
| 5. Wiederholung | 2 Stunden    |

<Note>
  Wiederholungen werden gestoppt, wenn eine `2xx`-Antwort empfangen wird oder die `retry_count`-Anzahl erschoepft ist. Der Standard-Wiederholungszaehler betraegt 3.
</Note>

### Zustellungsverlauf einsehen

Pruefen Sie das Zustellungsprotokoll eines Webhooks, um vergangene Zustellungsversuche und deren Ergebnisse einzusehen.

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

**Antwort:**

```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"
    }
  ]
}
```

***

## Beispiele fuer Ereignis-Payloads

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

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Schnell antworten" icon="bolt">
    Geben Sie innerhalb von 5 Sekunden einen `200`-Status zurueck. Verarbeiten Sie das Ereignis asynchron, um Timeouts zu vermeiden.
  </Card>

  <Card title="Deduplizieren" icon="clone">
    Verwenden Sie die Zustellungs-`id`, um doppelte Zustellungen durch Wiederholungen zu erkennen und zu ueberspringen.
  </Card>

  <Card title="Signaturen verifizieren" icon="shield-halved">
    Validieren Sie immer den `X-Wappfy-Signature`-Header, wenn Sie ein Geheimnis konfiguriert haben.
  </Card>

  <Card title="HTTPS verwenden" icon="lock">
    Webhook-URLs muessen HTTPS verwenden. HTTP-Endpunkte werden abgelehnt.
  </Card>
</CardGroup>
