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

# Livraison des webhooks

> Politique de retry, codes acceptés, SLO, validation x-webhook-key, debug.

Cette page documente le contrat de livraison côté Fluximmo : ce que votre endpoint doit renvoyer, combien de fois on retente, et comment sécuriser la réception.

<Warning>
  **Important : avant de créer une alerte, vérifier que le webhook répond bien `200` aux données de sample disponibles sans auth (`GET /v2/sample/webhook/{adverts|properties}`).**
</Warning>

## Politique de retry

Quand votre endpoint ne répond pas avec un code 2xx valide (voir plus bas), Fluximmo retente la livraison selon le calendrier suivant :

| Tentative          | Délai depuis le précédent | Cumulé approx.   |
| ------------------ | ------------------------- | ---------------- |
| 1 (initiale)       | —                         | T0               |
| 2 (retry immédiat) | quelques secondes         | \~5 s            |
| 3 (retry immédiat) | quelques secondes         | \~10 s           |
| 4 à 13             | 5 min                     | jusqu'à \~50 min |
| **DROP**           | —                         | après \~50 min   |

<Warning>
  Au bout de **2 retries immédiats + 10 retries espacés de 5 minutes** (≈ 50 minutes au total), le webhook est définitivement abandonné. Aucune relivraison automatique au-delà.
</Warning>

## Codes considérés OK

| Code HTTP renvoyé                        | Interprétation Fluximmo                              |
| ---------------------------------------- | ---------------------------------------------------- |
| `200`, `201`, `202`, `203`, `204`, `205` | Livraison acquittée — pas de retry                   |
| Tout le reste (3xx, 4xx, 5xx, timeout)   | Échec — retry programmé selon la politique ci-dessus |

<Tip>
  Renvoyez `204 No Content` (ou `200`) **immédiatement après réception**, puis traitez le payload de manière asynchrone côté worker. C'est le pattern le plus robuste.
</Tip>

## SLO côté client

* **Ack \< 1 seconde recommandé** : au-delà, la requête peut être considérée comme un timeout côté Fluximmo et déclencher un retry.
* **Pas de traitement métier dans le handler HTTP** : pousser le payload dans une file (Redis, SQS, RabbitMQ…) et acquitter immédiatement.
* **Idempotence requise** : un même `flxId` peut être livré plusieurs fois (retry sur ack lent, sur 5xx transitoire). Utilisez un UPSERT par `flxId` côté worker.

## Validation du header `x-webhook-key`

Chaque webhook URL est associée à un secret partagé (généré à la création de l'alerte / abonnement). Fluximmo l'envoie dans le header HTTP **`x-webhook-key`**.

<Warning>
  **Validez systématiquement** ce header avant de traiter le payload. Un endpoint webhook public sans validation est une cible de spoofing.
</Warning>

<CodeGroup>
  ```python Python theme={null}
  import os
  import secrets
  from flask import Flask, request, abort

  app = Flask(__name__)
  EXPECTED_KEY = os.environ["FLUXIMMO_WEBHOOK_KEY"]

  @app.post("/webhook/fluximmo")
  def receive():
      received = request.headers.get("x-webhook-key", "")
      if not secrets.compare_digest(received, EXPECTED_KEY):
          abort(401)

      # Pousser dans la queue, acquitter immédiatement
      queue.publish(request.get_json())
      return "", 204
  ```

  ```javascript Node.js theme={null}
  import express from "express";
  import crypto from "node:crypto";

  const app = express();
  const EXPECTED = Buffer.from(process.env.FLUXIMMO_WEBHOOK_KEY);

  app.post("/webhook/fluximmo", express.json(), (req, res) => {
    const received = Buffer.from(req.header("x-webhook-key") ?? "");

    if (received.length !== EXPECTED.length ||
        !crypto.timingSafeEqual(received, EXPECTED)) {
      return res.sendStatus(401);
    }

    queue.publish(req.body);
    res.sendStatus(204);
  });
  ```
</CodeGroup>

<Note>
  Utilisez **`secrets.compare_digest`** (Python) ou **`crypto.timingSafeEqual`** (Node) pour éviter les attaques temporelles. Une comparaison `==` classique est exploitable.
</Note>

## Idempotence

Les retries sont identiques (même payload, même `flxId`). Côté persistance :

* **UPSERT par `flxId`** : `INSERT ... ON CONFLICT (flx_id) DO UPDATE` (PostgreSQL) ou équivalent.
* **Tracker des `webhookId` déjà vus** si vous avez besoin d'un dédoublonnage strict (rare).
* **Webhooks Properties** : ne contiennent que des IDs — refetch via `GET /v2/protected/properties/{flxId}` ou l'endpoint bulk.

## Debug

Si vous suspectez des webhooks perdus :

1. **Vérifier les logs côté client** : codes HTTP renvoyés, latence de réponse, statut du worker.
2. **Tester manuellement** votre endpoint avec un payload type (voir `/__samples/` ou les exemples de l'API reference).
3. **Vérifier que le header `x-webhook-key`** n'est pas en conflit avec une couche réseau (proxy, WAF) qui le strip.
4. **Contacter le support** ([contact@fluximmo.com](mailto:contact@fluximmo.com)) avec : URL endpoint, période suspectée, alertId concernée. Le replay manuel n'est pas garanti une fois le DROP atteint.

<Info>
  Pour une architecture haut volume (file de message, idempotence, reprise sur crash), voir le playbook [Architecture webhook haut volume](/playbooks/webhook-volume-architecture).
</Info>
