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

# Webhooks API immobilier — retry, signature, payload

> Architecture webhooks Fluximmo : retry, signature, IDs vs payload complet, bonnes pratiques de réception.

Un webhook Fluximmo est un `POST` HTTPS émis par notre infrastructure vers une URL que vous fournissez à la création d'une **alerte**. Il porte les matches d'annonces (nouveaux biens, changements de prix, mises hors ligne, fusions) en quasi temps réel — typiquement dans la minute qui suit l'ingestion d'une annonce ou l'occurrence d'un événement.

## Pipeline de livraison

```mermaid theme={null}
flowchart LR
  A["Alerte<br/>match"] --> Q1["Queue interne<br/>Fluximmo"]
  Q1 --> P["POST<br/>x-webhook-key"]
  P --> R{"HTTP 2xx<br/>en moins de 10 s ?"}
  R -- "OUI" --> OK["Livré"]
  R -- "NON / timeout" --> RT["Retry policy<br/>2 immédiats + 10 x 5 min"]
  RT -- "succès" --> OK
  RT -- "epuise" --> DROP["DROP apres ~50 min"]
```

**Côté client, le pattern recommandé est inverse** : ne traitez jamais le webhook dans la requête HTTP, **ackez en moins de 100 ms** et empilez le payload dans une queue durable (RabbitMQ, SQS, Kafka, Redis Streams). Un worker async fait ensuite le travail métier.

## Payloads : Adverts vs Properties

| Aspect                   | Adverts                                                        | Properties                                       |
| ------------------------ | -------------------------------------------------------------- | ------------------------------------------------ |
| **Contenu du payload**   | `AdvertDto` complet (créations) + DTO réduit (updates)         | **IDs uniquement** (`propertyFlxId[]`)           |
| **Refetch nécessaire ?** | Non pour les créations — voir note ci-dessous pour les updates | **Oui** : `GET /v2/protected/properties/{flxId}` |
| **Volume réseau**        | Élevé (jusqu'à plusieurs Mo par batch)                         | Faible (liste d'IDs)                             |
| **Latence métier**       | Faible (zéro round-trip API)                                   | +1 round-trip HTTP par property                  |
| **Cas d'usage typique**  | Monitoring temps réel, miroir BDD                              | Analytics, déduplication, valorisation           |

Pourquoi cette asymétrie ? Une `Property` est une entité agrégée qui peut évoluer entre l'émission et la réception du webhook (un nouvel advert peut s'y rattacher). Refetcher garantit la version la plus fraîche au moment où vous la traitez.

### Shape canonique (côté ADVERT)

Le payload reçu est toujours wrappé sous `data` avec deux branches `created` (nouvelles adverts matchées) et `updated` (changements sur des adverts précédemment matchées) :

```json theme={null}
{
  "data": {
    "created": [
      {
        "alert_id": "alr_xxx",
        "adverts": [ /* AdvertDto complet : flxId, propertyFlxId, currentPrice, habitation, location, source, isOnline... */ ]
      }
    ],
    "updated": [
      {
        "alert_id": "alr_xxx",
        "adverts": [ /* DTO réduit : flxId, currentPrice (value + valuePerArea), isOnline */ ]
      }
    ]
  }
}
```

<Note>
  **Events PRICE / REPUBLISHED / UNPUBLISHED ne sont pas explicitement émis.** Ils se **dérivent côté client** en comparant les adverts de `data.updated[]` avec votre état local stocké : `currentPrice.value` qui change ⇒ PRICE, `isOnline` qui flip ⇒ REPUBLISHED ou UNPUBLISHED. Voir [Match types & cycle alerte](/concepts/match-types-cycle-alerte) et le [playbook track prix](/playbooks/track-price-changes).
</Note>

## Sécurité — header `x-webhook-key`

Chaque webhook porte un header `x-webhook-key` que vous avez défini à la création de l'alerte (champ `webhook.headerValue` ou équivalent dans le payload alerte). Validez-le côté client avant tout traitement.

<CodeGroup>
  ```python Python (FastAPI) theme={null}
  from fastapi import FastAPI, Request, Header, HTTPException
  import os

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

  @app.post("/webhook/fluximmo")
  async def handler(req: Request, x_webhook_key: str = Header(default="")):
      if x_webhook_key != EXPECTED_KEY:
          raise HTTPException(status_code=401, detail="invalid key")
      payload = await req.json()
      # ack rapide → enqueue → 200
      await enqueue(payload)
      return {"ok": True}
  ```

  ```js Node (Express) theme={null}
  const EXPECTED_KEY = process.env.FLUXIMMO_WEBHOOK_KEY;

  app.post("/webhook/fluximmo", express.json({ limit: "10mb" }), async (req, res) => {
    if (req.header("x-webhook-key") !== EXPECTED_KEY) {
      return res.status(401).end();
    }
    await queue.publish(req.body);
    res.status(200).end();
  });
  ```
</CodeGroup>

<Warning>
  N'effectuez **aucun traitement métier synchrone** dans le handler HTTP : pas d'appel BDD lourd, pas d'appel API tiers, pas d'envoi d'email. Acceptez vite, faites le travail dans un worker.
</Warning>

## Politique de retry

| Étape              | Délai                | Détail                                  |
| ------------------ | -------------------- | --------------------------------------- |
| Tentative initiale | t = 0                | Timeout HTTP côté Fluximmo              |
| Retries immédiats  | t + \~quelques s     | 2 retries rapides après échec           |
| Retries longs      | toutes les 5 min     | 10 retries supplémentaires              |
| **DROP**           | \~50 min après t = 0 | Le webhook est définitivement abandonné |

**Codes HTTP considérés OK** : `200`, `201`, `202`, `203`, `204`, `205`. Tout autre code (4xx, 5xx) déclenche un retry.

**SLO côté client** : votre handler doit répondre en **30 à 50 secondes maximum**. Au-delà, Fluximmo timeout et entre en retry. Un handler bloquant à 30 s sous charge devient une cascade d'échecs.

<Note>
  **Idempotence obligatoire.** Vous recevrez parfois le même webhook deux fois (retry après timeout côté réseau alors que le serveur avait déjà accepté). Dédupliquez côté client avec une clé stable, par exemple `(advert.flxId, branch, alert_id)` où `branch` ∈ `{ "created", "updated" }`, ou `(advert.flxId, currentPrice.value, isOnline)` si vous voulez aussi capturer les diffs.
</Note>

## Architecture recommandée

```mermaid theme={null}
flowchart LR
  W["POST webhook<br/>Fluximmo"] --> H["HTTP handler<br/>(stateless)"]
  H -->|valide x-webhook-key| Q["Durable queue<br/>SQS / RabbitMQ"]
  H -->|200 OK <100 ms| W
  Q --> WK["Async worker<br/>(scalable)"]
  WK --> DB["BDD client"]
  WK --> NOTIF["Notif user<br/>email / push"]
```

Le détail (sizing queue, dead-letter, observabilité, scaling worker) sera couvert dans le playbook [Architecture haute volumétrie](/playbooks/webhook-volume-architecture).

## Pour aller plus loin

* [Match types & cycle de vie d'une alerte](/concepts/match-types-cycle-alerte) — quels events arrivent dans quel webhook.
* [Webhook livraison — règles détaillées](/ressources/webhooks-livraison)
* [Playbook — Architecture webhook haute volumétrie](/playbooks/webhook-volume-architecture)

<Snippet file="/snippets/cta-cle-test-gratuite.mdx" />
