Aller au contenu principal

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 (important) les livraisons webhook ne comptent pas dans votre quota mensuel de requêtes.

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. On POST dessus à chaque création de commande. Latence ~2-5 s. Zéro polling. Zéro gâchis de quota.

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

Commande/événement à l'origine


INSERT row dans api_v1_webhook_deliveries


Cron drain chaque minute (next_retry_at <= NOW())


Signe HMAC du body ─────► POST votre URL avec timeout 10 s
│ │
│ ├─ 2xx → marque livré, fini
│ ├─ 5xx → retry : 1m, 5m, 30m, 2h, 12h
│ ├─ 4xx → ack comme poison ; PAS de retry
│ └─ pas de réponse (timeout / DNS) → retry

└─ 5 échecs → DLQ

Calendrier de retry

Après chaque tentative échouée, next_retry_at est mis à now() + delay[attempt] :

TentativeDélai
11 minute
25 minutes
330 minutes
42 heures
512 heures
6eDLQ — intervention manuelle

Fenêtre totale de retry ~14,5 h. Au-delà, la livraison part en DLQ et failure_count du webhook s'incrémente. Cinq DLQ consécutifs auto-désactivent le webhook (status → dead) ; contactez le support pour reset.

Ce qui compte comme un « succès »

  • HTTP 200, 201, 202, 204 (n'importe quel 2xx) — succès.
  • HTTP 4xx (400, 401, 403, 404, 422 …) — pas de retry. On traite 4xx comme « votre endpoint rejette définitivement cette payload » ; les retries n'aideront pas. Fixez votre endpoint et re-testez via POST /v1/webhooks/{id}/test.
  • HTTP 5xx, timeout, échec DNS, échec TLS — retry.

Si vous voulez qu'on retry sur un 4xx précis (rare), répondez 503 Service Unavailable au lieu d'un 4xx.

Modèle de sécurité

Ce qu'on envoie

Content-Type: application/json
User-Agent: dzbuild-webhook/1
X-DZ-Timestamp: <unix seconds>
X-DZ-Signature: <hex hmac-sha256>
X-DZ-Delivery-Id: <id>

Signature

X-DZ-Signature = hex( hmac_sha256( WEBHOOK_SECRET, X-DZ-Timestamp + "." + sha256(body) ) )

Le WEBHOOK_SECRET est ce que vous avez récupéré en enregistrant le webhook (POST /v1/webhooks). On ne le réexpose jamais — stockez-le.

Ce que vous devez faire

  1. Vérifier la signature sur chaque requête (voir Vérifier les signatures).
  2. Vérifier que le timestamp est dans 5 min de l'horloge serveur — rejette les rejeux.
  3. Utiliser les bytes raw du body pour le hash — ne re-sérialisez pas le JSON.
  4. Soyez idempotentX-DZ-Delivery-Id est unique par tentative, mais le même événement logique PEUT être livré plusieurs fois (réseau, retries-après-succès). Dédupliquez côté vous via delivery_id ou les ids de l'event.

Ce qu'on ne fait pas

  • Pas d'auth sortant en mTLS. Si votre endpoint le requiert, mettez un reverse proxy qui strip/ajoute mTLS devant.
  • Pas de IP-lockout. On envoie depuis CF Workers — l'IP source est dans les ranges Cloudflare. Pour whitelist, utilisez la liste IP Cloudflare.

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": "abc123def456"
}
ChampNotes
eventType d'événement (liste complète dans Catalogue d'événements).
store_idVotre store id — utile si vous avez plusieurs webhooks sur le même handler.
occurred_atQuand l'événement a eu lieu chez nous, ISO 8601 + TZ.
dataPayload propre à l'événement. Voir Catalogue pour la forme.
delivery_idUnique par tentative. Utilisez-le pour l'idempotence.

Quota

Chaque tentative de livraison — succès, 4xx, retry — compte 1 dans webhooks_per_month. Un seul event qui retry 5 fois coûte 5. Donc :

  • Gardez votre endpoint fiable (2xx rapide) → 1 tentative par event.
  • Les échecs coûtent plus cher → fixez les bugs vite pour ne pas brûler le quota.

La suite