# Webhooks

Les webhooks sont des notifications push temps réel de DZBuild vers **votre** serveur quand quelque chose se passe sur votre boutique. Utilisez-les au lieu du polling — moins de charge, latence plus basse, et les livraisons webhook ne comptent pas dans votre quota mensuel de **requêtes**.

Il existe deux systèmes de webhooks DZBuild distincts

Choisissez le bon avant de construire.

|                        | **Addon Webhooks marchand**                                                                                                                                                                                                                    | **Webhooks API v1** (cette section)                                                                                                                                                                                                                                                                                                        |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Configuration          | `/dashboard/webhooks` — sans code                                                                                                                                                                                                              | `POST /v1/webhooks` — via l'API uniquement                                                                                                                                                                                                                                                                                                 |
| Qui peut l'utiliser    | Plan Unlimited et au-delà                                                                                                                                                                                                                      | Les boutiques avec un plan Enterprise actif, via une clé API du marchand (les jetons d'application installée sont refusés)                                                                                                                                                                                                                 |
| Endpoints par boutique | 1 en Unlimited, 3 en Enterprise                                                                                                                                                                                                                | Aucune limite n'est appliquée                                                                                                                                                                                                                                                                                                              |
| Signature              | `X-DZ-Signature: t=<ts>,v1=hex(hmac_sha256(secret, ts + "." + rawBody))` sur le **body brut** : vous pouvez la vérifier. Envoie aussi un en-tête `X-DZ-Token: <secret>` pour les outils no-code qui ne savent faire que de l'auth par en-tête. | Même format : `X-DZ-Signature: t=<ts>,v1=hex(hmac_sha256(secret, ts + "." + rawBody))` sur le **body brut**, avec le `secret` renvoyé une seule fois par `POST /v1/webhooks`. Vous pouvez la vérifier. Les webhooks enregistrés avant la signature par webhook gardent une ancienne signature invérifiable : voir [Signature](#signature). |
| Commandes couvertes    | `order.created` depuis **toutes** les sources (boutique, landing page, création manuelle au dashboard, API) plus 6 événements de statut, dont `order.processing`                                                                               | Les commandes créées ou mises à jour **via l'API**, plus les confirmations et annulations faites avec les boutons des notifications Telegram de nouvelle commande, commandes de la boutique et des landing pages comprises                                                                                                                 |
| Livré depuis           | Les plages IP Cloudflare                                                                                                                                                                                                                       | Les adresses de sortie propres à DZBuild — demandez la liste actuelle au support                                                                                                                                                                                                                                                           |
| Extras                 | Interface de journal des livraisons, régénération du secret, validation HTTPS de la cible, désactivation automatique après 10 échecs consécutifs                                                                                               | Validation HTTPS de la cible                                                                                                                                                                                                                                                                                                               |

Si tout ce dont vous avez besoin, ce sont des notifications de commande fiables, l'addon est le meilleur produit. Utilisez les webhooks API v1 quand votre intégration parle déjà à l'API REST.

## Pourquoi des webhooks[​](#pourquoi-des-webhooks "Lien direct vers Pourquoi des webhooks")

Comparez :

**Polling** — votre code appelle `GET /v1/orders?since=...` chaque minute. 1440 appels/jour, 1440 round-trips, votre quota brûle uniformément, et la latence « commande créée → votre code le sait » est de 60 s.

**Webhooks** — vous enregistrez `https://yourapp/webhooks` une fois. Chaque commande créée via `POST /v1/orders` met une livraison en file, et la file se vide en continu : la latence est donc **généralement inférieure à une minute**. Zéro polling, zéro gâchis de quota.

Les webhooks API v1 ignorent les commandes passées sur la boutique et les changements faits au dashboard

`order.created` se déclenche **uniquement** pour les commandes créées via `POST /v1/orders`. Les commandes passées sur la boutique, sur une landing page ou créées manuellement au dashboard ne déclenchent rien ici. Les changements de statut faits au dashboard ne déclenchent rien non plus ; seuls les changements faits via l'API et les boutons de confirmation et d'annulation des notifications Telegram de nouvelle commande en déclenchent. Voir le [Catalogue d'événements](https://dzbuild.com/fr/fr/api-docs/webhooks/events.md).

Le polling n'est meilleur que quand :

* Votre endpoint n'est pas joignable depuis Internet (pollez depuis votre réseau interne).
* Vous n'avez pas de serveur (pollez depuis une lambda planifiée / cron).

## Comment fonctionne la livraison[​](#comment-fonctionne-la-livraison "Lien direct vers Comment fonctionne la livraison")

```
Une écriture API v1 a lieu

        │

        ▼

Une livraison est mise en file pour chaque webhook abonné à cet événement

        │

        ▼

La file se vide en continu — généralement en moins d'une minute

        │

        ▼

Body signé  ─────►  POST votre URL (5 s connexion, 10 s au total)

                              │

                              ├─ 2xx : marque livré, fini

                              ├─ 5xx, 408, 429, timeout, échec DNS ou TLS : re-tenté avec backoff

                              ├─ tout autre 4xx, ou un 3xx : file morte immédiatement, pas de retry

                              └─ 5 tentatives échouées : file morte, plus de retry
```

### Comportement des retries[​](#comportement-des-retries "Lien direct vers Comportement des retries")

* Les réponses **5xx**, **408** et **429** et les échecs réseau (timeout, DNS, TLS) sont réessayés 1 min, 5 min, 30 min puis 2 h après l'échec. Après le 5e échec, la livraison passe en file morte et n'est plus envoyée.
* Tout autre **4xx**, et tout **3xx**, n'est tenté qu'une fois : la livraison passe aussitôt en file morte et n'est jamais réessayée. Les redirections ne sont pas suivies : un `301`/`302` compte comme un échec.

Il n'y a pas de désactivation automatique. Le `failure_count` du webhook s'incrémente une fois par **tentative** échouée et retombe à `0` au premier succès ; `status` reste `active`.

Conséquences pratiques :

* **Répondez 2xx vite.** Si vous ne pouvez pas traiter une payload, répondez quand même 2xx et jetez-la ; un 5xx vous coûte quatre livraisons de plus du même body sur environ 2 h 30.
* **Ne comptez pas sur les retries pour un endpoint lent.** Cinq échecs en 2 h 30 environ et la livraison est abandonnée. Persistez le body dans votre propre file et acquittez immédiatement.
* **Réconciliez par polling.** Comme une livraison est abandonnée après cinq échecs, lancez un balayage périodique `GET /v1/orders?since=...` comme filet de sécurité. `since` filtre sur la date de création de la commande : le balayage retrouve les commandes dont vous avez manqué le `order.created` ; pour un changement de statut manqué, relisez les commandes que vous suivez encore et comparez leur `status`.

### Ce qui compte comme un « succès »[​](#ce-qui-compte-comme-un--succès- "Lien direct vers Ce qui compte comme un « succès »")

* HTTP 200, 201, 202, 204 (n'importe quel 2xx) — succès.
* HTTP 4xx autre que `408` et `429` (400, 401, 403, 404, 422, etc.) : **pas de retry**, la livraison passe directement en file morte. Corrigez votre endpoint et re-testez via `POST /v1/webhooks/{id}/test`.
* HTTP 3xx : **pas de retry**, file morte immédiate. Nous ne suivons pas les redirections ; pointez le webhook sur l'URL finale.
* Timeout, échec DNS, échec TLS : re-tentés comme un 5xx. Les certificats TLS sont vérifiés strictement : un certificat auto-signé échoue à chaque tentative.
* HTTP 5xx, `408` et `429` : re-tentés selon le calendrier ci-dessus, 5 tentatives au total.

## Modèle de sécurité[​](#modèle-de-sécurité "Lien direct vers Modèle de sécurité")

### Ce qu'on envoie[​](#ce-quon-envoie "Lien direct vers Ce qu'on envoie")

```
Content-Type:    application/json

User-Agent:      dzbuild-webhook/1

X-DZ-Timestamp:  <unix seconds>

X-DZ-Signature:  t=<unix seconds>,v1=<hex hmac-sha256>

X-DZ-Delivery-Id: <numeric delivery id>
```

### Signature[​](#signature "Lien direct vers Signature")

`X-DZ-Signature` est calculée avec le `secret` que `POST /v1/webhooks` vous a renvoyé à l'enregistrement du webhook :

```
X-DZ-Signature: t=<unix seconds>,v1=<hex hmac-sha256>



expected = hex( hmac_sha256( WEBHOOK_SECRET, t + "." + raw_body ) )



if (!constant_time_equal(expected, v1)) reject 401

if (abs(now - t) > 300)                 reject 401   # fenêtre rejeu ±5 min
```

Pour vérifier une livraison :

1. Découpez l'en-tête sur `,` et lisez les valeurs `t` et `v1`.
2. Calculez le HMAC-SHA256 de `t + "." + raw_body` avec le secret du webhook, `raw_body` étant exactement les octets reçus, puis encodez-le en hex.
3. Comparez le résultat à `v1` à temps constant.
4. Rejetez la livraison si `t` s'écarte de plus de 300 secondes de votre horloge.

Chaque tentative est signée au moment de l'envoi : un retry porte donc un nouveau `t` et un nouveau `v1` sur le même body. Le code en quatre langages est sur [Vérifier les signatures](https://dzbuild.com/fr/fr/api-docs/webhooks/verifying-signatures.md).

Webhooks enregistrés avant la signature par webhook

Un webhook créé avant que DZBuild ne signe avec le secret propre à chaque webhook garde l'ancienne signature : une valeur hex nue sans partie `t=`, calculée avec une clé que DZBuild ne communique pas. Vous ne pouvez pas la vérifier. Supprimez ce webhook et enregistrez-le de nouveau : le nouvel enregistrement renvoie un secret qui signe chaque livraison.

### Ce que vous devez faire[​](#ce-que-vous-devez-faire "Lien direct vers Ce que vous devez faire")

1. **Vérifiez la signature avant d'agir.** Rejetez toute livraison dont le `v1` ne correspond pas, avant de parser le JSON ou de toucher à une commande.
2. **Servez du HTTPS.** `POST /v1/webhooks` refuse toute `url` qui ne commence pas par `https://`, et les certificats sont vérifiés strictement.
3. **Vérifiez que le timestamp est dans 5 min** de l'horloge serveur — protection anti-rejeu à moindre coût.
4. **Utilisez les bytes raw du body** pour le HMAC. Ne re-sérialisez pas le JSON.
5. **Soyez idempotent.** La même livraison peut arriver plusieurs fois : après un 5xx, un `408`, un `429` ou un timeout, elle est renvoyée avec le même body. Dédupliquez uniquement sur le `delivery_id` du body signé.

### Ce qu'on **ne** fait **pas**[​](#ce-quon-ne-fait-pas "Lien direct vers ce-quon-ne-fait-pas")

* Pas d'auth sortant en mTLS. Si votre endpoint le requiert, mettez un reverse proxy qui strip/ajoute mTLS devant.
* On n'envoie **pas** les livraisons API v1 depuis les plages IP Cloudflare : n'autoriser que celles-ci bloque donc toutes les livraisons. S'il vous faut une allow-list IP, demandez au support les adresses de sortie actuelles — elles peuvent changer. (L'addon Webhooks marchand, lui, part *bien* des plages Cloudflare.)

## Enveloppe de payload[​](#enveloppe-de-payload "Lien direct vers Enveloppe de payload")

Chaque body webhook a la même forme externe :

```
{

  "event":       "order.confirmed",

  "store_id":    13,

  "occurred_at": "2026-04-30T21:18:21+00:00",

  "data":        { "order_id": 6894, "old_status": "pending", "new_status": "confirmed" },

  "delivery_id": "9f2c41ab77e05d18"

}
```

| Champ         | Notes                                                                                                                                                                                          |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event`       | Type d'événement (liste complète dans [Catalogue d'événements](https://dzbuild.com/fr/fr/api-docs/webhooks/events.md)).                                                                        |
| `store_id`    | Votre store id — utile si vous avez plusieurs webhooks sur le même handler.                                                                                                                    |
| `occurred_at` | Quand l'événement a eu lieu chez nous, ISO 8601 + TZ.                                                                                                                                          |
| `data`        | Payload propre à l'événement. Voir [Catalogue](https://dzbuild.com/fr/fr/api-docs/webhooks/events.md) pour la forme.                                                                           |
| `delivery_id` | Chaîne de 16 caractères hex, unique par **livraison** (un webhook × un événement). Elle est identique octet pour octet à chaque retry — c'est ce qui la rend utilisable pour la déduplication. |

L'en-tête `X-DZ-Delivery-Id` est une valeur **différente** : un id numérique de livraison, par exemple `4127`. Il est stable d'un retry à l'autre, mais la signature ne le couvre pas : quiconque atteint votre URL peut y mettre n'importe quelle valeur. Dédupliquez uniquement sur le `delivery_id` du body signé.

## Quota[​](#quota "Lien direct vers Quota")

Les livraisons webhook ne sont aujourd'hui **ni comptabilisées ni plafonnées**. Une valeur `webhooks_per_month` est remontée par `GET /v1/usage` et `GET /v1/quotas`, mais rien ne l'incrémente et rien ne l'applique. Les tentatives de livraison ne consomment pas non plus votre quota API `requests_per_month`.

Ce n'est pas un permis d'être lent : un endpoint qui répond 5xx reçoit le même body jusqu'à quatre fois de plus sur environ 2 h 30 (voir [Comportement des retries](#comportement-des-retries)).

## La suite[​](#la-suite "Lien direct vers La suite")

* [Enregistrement](https://dzbuild.com/fr/fr/api-docs/webhooks/registering.md) — `POST /v1/webhooks` avec body, réponse, exemples.
* [Catalogue d'événements](https://dzbuild.com/fr/fr/api-docs/webhooks/events.md) — chaque event avec sample data.
* [Vérifier les signatures](https://dzbuild.com/fr/fr/api-docs/webhooks/verifying-signatures.md) — code en 4 langages.
