L'API v1 n'applique aucune limite de webhooks par boutique — enregistrez autant d'URLs que votre intégration en a réellement besoin, et supprimez celles que vous n'utilisez plus. (L'addon Webhooks marchand sans code, lui, plafonne bien les endpoints : 1 en Unlimited, 3 en Enterprise.)
Chaque webhook peut s'abonner à une liste différente d'événements ; choisissez un modèle qui vous convient :
Un seul endpoint, tous les events — le plus simple pour des petites apps. Branchez sur
eventdans le handler.Plusieurs endpoints, un event chacun — plus propre en microservices, mais plus d'URLs à gérer.
⚠️ Attention — Accès pilote
Les webhooks API v1 demandent une clé API du marchand, d'une boutique avec un plan Enterprise actif ; tout autre plan, ou un abonnement expiré, reçoit 403 forbidden "API access requires an active Enterprise plan". Les clés générées sur /dashboard/api sont déjà inscrites au pilote ; une clé non inscrite reçoit 403 forbidden "API is in pilot mode; key not enrolled". Les jetons d'application installée reçoivent 403 forbidden "Apps cannot use this endpoint" sur chaque appel sous /v1/webhooks.
POST /v1/webhooks — enregistrer
Auth : clé plateforme avec webhooks:write. Nécessite Idempotency-Key. Contrairement à la plupart des écritures, cette réponse n'est jamais mise en cache pour le rejeu, car elle contient le secret : réessayer avec la même clé enregistre un second webhook avec un nouveau secret. Si un appel expire, vérifiez GET /v1/webhooks avant de réessayer et supprimez tout doublon.
Corps
Champ | Type | Requis | Notes |
| string (URL https) | ✅ | Doit être |
| string[] | ✅ | Liste de noms d'events. Voir Catalogue d'events. Vide = erreur. |
Requête
curl -X POST 'https://api.dzbuild.app/v1/webhooks' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"url": "https://yourapp.example/webhooks/dzbuild",
"events": ["order.created", "order.confirmed", "order.shipped",
"order.cancelled", "signup.counted"]
}'
Réponse 200
{
"data": {
"id": 17,
"secret": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"note": "Save the secret now — it is not retrievable after this response."
},
"meta": { "request_id": "...", "api_version": "v1" }
}
💡 Astuce — Ne branchez jamais sur == 201
Les endpoints de création /v1/webhooks, /v1/products, /v1/orders, /v1/landing-pages et /v1/keys répondent 200 en cas de succès, tandis que d'autres, comme /v1/categories, répondent 201. Branchez plutôt sur n'importe quel 2xx.
Le secret est affiché UNE FOIS. Stockez-le à côté de l'id du webhook dans votre coffre : chaque livraison vers ce webhook est signée avec lui (voir Signature). Si vous le perdez, supprimez le webhook et créez-en un nouveau.
Erreurs
HTTP | Code / Message | Cause |
400 |
| URL mal formée |
400 |
| Une URL en |
400 |
| Une adresse IP au lieu d'un nom d'hôte |
400 |
| Une partie |
400 |
| Un hôte qui n'est pas un nom de domaine complet, comme |
400 |
| Le nom d'hôte n'a aucun enregistrement A ou AAAA |
400 |
| L'hôte pointe vers au moins une adresse privée ou réservée |
400 |
| Un port personnalisé comme |
400 |
| Le body de la requête n'est pas du JSON valide |
400 |
| Tableau vide |
400 |
| Nom d'event hors catalogue |
400 |
|
|
400 |
| Clé trop longue, ou contenant des caractères hors de cet ensemble (les |
403 |
| La clé existe mais n'est pas inscrite au pilote |
403 |
| La boutique n'a pas de plan Enterprise actif |
403 |
| Le jeton appartient à une application installée |
403 |
| La clé n'a pas le scope |
500 |
| Généralement une |
GET /v1/webhooks — liste
Auth : clé plateforme avec webhooks:read.
curl https://api.dzbuild.app/v1/webhooks \ -H "Authorization: Bearer $DZ_KEY"
{
"data": {
"items": [
{
"id": 17,
"url": "https://yourapp.example/webhooks/dzbuild",
"events": ["order.created", "order.confirmed"],
"status": "active",
"last_success_at": "2026-04-30 21:18:23",
"last_failure_at": null,
"failure_count": 0,
"created_at": "2026-04-30 19:00:00"
}
],
"allowed_events": [
"order.created", "order.confirmed", "order.shipped", "order.delivered",
"order.cancelled", "order.returned", "payment.received",
"signup.counted", "event.recorded", "product.stock_low"
]
}
}
allowed_events est la liste que l'API accepte à l'enregistrement — mais elle est plus large que ce qui se déclenche réellement. payment.received, event.recorded et product.stock_low sont acceptés puis jamais émis. Vérifiez le Catalogue d'événements avant de construire sur l'un d'eux.
Statut | Signification |
| Reçoit les livraisons. En pratique, c'est la seule valeur que vous verrez. |
| Réservé pour un usage futur, non utilisé actuellement — et il n'existe pas de page dashboard pour les webhooks API v1. |
| Réservé pour un usage futur, non utilisé actuellement. Il n'y a pas de désactivation automatique ; |
Pour arrêter les livraisons, supprimez le webhook.
POST /v1/webhooks/{id}/test
Déclenche une livraison webhook.test pour vérifier que votre endpoint est joignable et voir la forme de l'enveloppe.
webhook.test ne peut pas faire l'objet d'un abonnement : le mettre dans le tableau events à l'enregistrement renvoie 400 bad_request "unknown event: webhook.test. Allowed: …". Une livraison de test est envoyée au webhook ciblé quels que soient les événements auxquels il est abonné.
Auth : clé plateforme avec webhooks:write. Nécessite Idempotency-Key.
curl -X POST 'https://api.dzbuild.app/v1/webhooks/17/test' \ -H "Authorization: Bearer $DZ_KEY" \ -H "Idempotency-Key: test-17-$(date +%s)"
{ "data": { "tested": true, "note": "a webhook.test delivery was enqueued; check your endpoint" } }
La livraison de test ressemble à :
{
"event": "webhook.test",
"store_id": 13,
"occurred_at": "2026-04-30T21:24:17+00:00",
"data": { "ts": 1717112657 },
"delivery_id": "9f2c41ab77e05d18"
}
Deux choses à savoir :
Elle est mise en file, pas synchrone. La réponse confirme seulement que la livraison a été enfilée ; le POST arrive peu après, généralement dans la minute.
Elle teste votre code de vérification. Elle est signée avec le
secretde votre webhook comme toute autre livraison : si elle passe votre contrôle, elle prouve la joignabilité, la forme de la payload et la vérification de signature. Voir Signature.
DELETE /v1/webhooks/{id}
Auth : clé plateforme avec webhooks:write. Nécessite Idempotency-Key.
curl -X DELETE 'https://api.dzbuild.app/v1/webhooks/17' \ -H "Authorization: Bearer $DZ_KEY" \ -H "Idempotency-Key: del-17"
{ "data": { "deleted": true, "id": 17 } }
Après suppression : - Plus aucune livraison nouvelle n'est mise en file. - Toutes les livraisons déjà en file pour ce webhook sont abandonnées immédiatement. Rien n'est « tenté une dernière fois ». Si l'arriéré vous importe, suspendez vos écritures et laissez la file se vider (généralement moins d'une minute) avant de supprimer. - Le secret du webhook est désormais inutile.
Rejeu d'Idempotency-Key sur test et DELETE
L'API met en cache la réponse de chaque Idempotency-Key pendant 24 heures et la rejoue avec un en-tête Idempotency-Replay: 1. Deux conséquences :
Réutiliser une clé littérale comme
del-17dans les 24 heures rejoue la réponse en cache au lieu d'exécuter un nouvel appel. Utilisez une clé fraîche (ou une clé qui encode la tentative) dès que vous voulez vraiment que l'opération s'exécute.Les réponses d'erreur sont mises en cache elles aussi. Une écriture ratée rejoue son propre 4xx pendant 24 heures sous la même clé — changez de clé après avoir corrigé la requête.
Le rejeu est garanti pendant 24 heures quel que soit l'hôte appelé, et répond toujours avec Idempotency-Replay: 1. Préférez tout de même https://api.dzbuild.app/v1 : le chemin dzbuild.com/api/v1 n'est qu'un alias, et certains chemins peuvent y être bloqués.
Exigences de l'endpoint
Votre URL webhook doit :
Répondre en 2xx au succès. Un 5xx, un
408ou un429est re-tenté 1 min, 5 min, 30 min puis 2 h après l'échec, puis abandonné ; tout autre 4xx, et tout 3xx, envoie la livraison en file morte immédiatement (voir Comportement des retries).Répondre dans les 10 secondes au total (5 secondes pour la connexion). Plus lent compte comme un timeout, re-tenté comme un 5xx.
Être en
https://et servir un TLS valide. Les certificats sont vérifiés strictement : un certificat auto-signé échoue à chaque tentative.Ne pas rediriger.
Locationn'est pas suivi ; un301/302est un échec.Accepter
POSTavecContent-Type: application/json.Lire le body raw pour vérifier la signature (ne pas re-sérialiser).
Être idempotent — le même
delivery_idPEUT arriver plusieurs fois.
Piège classique dans certains frameworks : un middleware re-encode le JSON avant que votre handler le voie, et le HMAC ne match plus. Solutions :
Express :
express.raw({ type: 'application/json' })pour la route webhook, puisJSON.parse(req.body)dans le handler.Django :
request.bodyest les bytes raw — c'est ce qu'on veut.Laravel :
$request->getContent()retourne le body raw.PHP raw :
file_get_contents('php://input').
Voir Vérifier les signatures pour le code en 4 langages.