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

> Recevez des notifications en temps reel pour les messages, les evenements de livraison et les changements de statut des instances.

# Webhooks

Les webhooks vous permettent de recevoir des callbacks HTTP en temps reel lorsque des evenements se produisent sur vos instances WhatsApp. Au lieu d'interroger l'API, Wappfy pousse les evenements vers votre serveur au fur et a mesure qu'ils surviennent.

## Evenements pris en charge

Wappfy prend en charge 12 types d'evenements webhook :

<AccordionGroup>
  <Accordion title="Evenements de messages">
    | Evenement           | Description                                                                  |
    | ------------------- | ---------------------------------------------------------------------------- |
    | `message.received`  | Un nouveau message entrant a ete recu.                                       |
    | `message.sent`      | Un message sortant a ete envoye avec succes.                                 |
    | `message.delivered` | Un message envoye a ete livre sur l'appareil du destinataire (double coche). |
    | `message.read`      | Un message envoye a ete lu par le destinataire (coches bleues).              |
    | `message.failed`    | L'envoi d'un message sortant a echoue.                                       |
    | `message.reaction`  | Quelqu'un a reagi a un message avec un emoji.                                |
  </Accordion>

  <Accordion title="Evenements d'instance">
    | Evenement               | Description                                          |
    | ----------------------- | ---------------------------------------------------- |
    | `instance.connected`    | Une instance s'est connectee a WhatsApp avec succes. |
    | `instance.disconnected` | Une instance a perdu sa connexion WhatsApp.          |
    | `instance.qr`           | Un nouveau QR code est disponible pour le scan.      |
  </Accordion>

  <Accordion title="Evenements de groupes et contacts">
    | Evenement         | Description                                                     |
    | ----------------- | --------------------------------------------------------------- |
    | `group.joined`    | Un participant a rejoint un groupe (y compris le bot lui-meme). |
    | `group.left`      | Un participant a quitte un groupe.                              |
    | `contact.created` | Un nouveau contact a ete enregistre ou detecte.                 |
  </Accordion>
</AccordionGroup>

***

## Creer un webhook

Enregistrez un endpoint webhook pour commencer a recevoir des evenements.

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

**Reponse :**

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

### Options de configuration

| Champ         | Type      | Defaut          | Description                                                                                           |
| ------------- | --------- | --------------- | ----------------------------------------------------------------------------------------------------- |
| `url`         | string    | **obligatoire** | L'URL HTTPS qui recevra les requetes POST du webhook.                                                 |
| `events`      | string\[] | **obligatoire** | Tableau des types d'evenements auxquels s'abonner.                                                    |
| `instance_id` | string    | `null`          | Limiter le webhook a une instance specifique. Si null, recoit les evenements de toutes les instances. |
| `secret`      | string    | `null`          | Secret utilise pour generer des signatures HMAC pour la verification du contenu.                      |
| `retry_count` | number    | `3`             | Nombre de tentatives de reessai en cas d'echec de livraison (0-5).                                    |
| `timeout_ms`  | number    | `10000`         | Delai d'expiration de la requete en millisecondes (1000-30000).                                       |

<Note>
  Le champ `instance_id` est optionnel. S'il est omis, le webhook recevra les evenements de **toutes** les instances de votre compte.
</Note>

***

## Lister les webhooks

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

**Reponse :**

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

***

## Mettre a jour un webhook

Mettez a jour l'URL, les evenements ou la configuration d'un webhook existant.

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

***

## Supprimer un webhook

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

***

## Format du contenu de livraison

Lorsqu'un evenement se produit, Wappfy envoie une requete POST a votre URL webhook avec la structure suivante :

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

### Champs du contenu

| Champ         | Description                                                                   |
| ------------- | ----------------------------------------------------------------------------- |
| `id`          | Identifiant unique de la livraison. Utilisez-le pour la deduplication.        |
| `event`       | Le type d'evenement qui a declenche cette livraison.                          |
| `instance_id` | L'instance qui a genere l'evenement.                                          |
| `timestamp`   | Horodatage ISO 8601 du moment ou l'evenement s'est produit.                   |
| `data`        | Contenu specifique a l'evenement. Le contenu varie selon le type d'evenement. |

***

## Verification de la signature HMAC

Si vous fournissez un `secret` lors de la creation d'un webhook, chaque livraison inclura un en-tete `X-Wappfy-Signature` contenant une signature HMAC-SHA256 du corps de la requete.

**Verifiez toujours cette signature** pour vous assurer que la requete provient de Wappfy et n'a pas ete alteree.

### Exemples de verification

<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>
  Utilisez toujours une comparaison a temps constant (comme `timingSafeEqual` ou `hmac.compare_digest`) lors de la verification des signatures pour prevenir les attaques par timing.
</Warning>

***

## Comportement de reessai

Si votre serveur ne repond pas avec un code de statut `2xx` dans le delai configure `timeout_ms`, Wappfy reessaiera la livraison.

| Tentative   | Delai       |
| ----------- | ----------- |
| 1er reessai | 10 secondes |
| 2e reessai  | 60 secondes |
| 3e reessai  | 5 minutes   |
| 4e reessai  | 30 minutes  |
| 5e reessai  | 2 heures    |

<Note>
  Les reessais s'arretent lorsqu'une reponse `2xx` est recue ou que le `retry_count` est epuise. Le nombre de reessais par defaut est de 3.
</Note>

### Consulter l'historique de livraison

Consultez le journal de livraison d'un webhook pour voir les tentatives de livraison passees et leurs resultats.

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

**Reponse :**

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

***

## Exemples de contenu par evenement

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

***

## Bonnes pratiques

<CardGroup cols={2}>
  <Card title="Repondez rapidement" icon="bolt">
    Retournez un statut `200` dans les 5 secondes. Traitez l'evenement de maniere asynchrone pour eviter les depassements de delai.
  </Card>

  <Card title="Dedupliquez" icon="clone">
    Utilisez l'`id` de livraison pour detecter et ignorer les livraisons en double causees par les reessais.
  </Card>

  <Card title="Verifiez les signatures" icon="shield-halved">
    Validez toujours l'en-tete `X-Wappfy-Signature` si vous avez configure un secret.
  </Card>

  <Card title="Utilisez HTTPS" icon="lock">
    Les URL de webhook doivent utiliser HTTPS. Les endpoints HTTP seront rejetes.
  </Card>
</CardGroup>
