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
L'API v1 est réservée au pilote en production. Une clé valide ne suffit pas — DZBuild doit l'inscrire au pilote, sinon chaque appel renvoie 403 forbidden "API is in pilot mode; key not enrolled".
POST /v1/webhooks — enregistrer
Auth : clé plateforme avec webhooks:write. Nécessite Idempotency-Key.
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": "fc9b5f0b51b4a93c1d6f8e29b6a2e30c7c2c44a4f2a6c8d8e0e1b9d4f6c1a8b3",
"note": "Save the secret now — it is not retrievable after this response."
},
"meta": { "request_id": "...", "api_version": "v1" }
}
💡 Astuce — Ne branchez jamais sur == 201
Aucun endpoint de création en v1 ne renvoie 201 aujourd'hui — /v1/webhooks, /v1/products, /v1/orders, /v1/landing-pages et /v1/keys répondent tous 200 en cas de succès. 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. Notez qu'il n'est pas actuellement la clé avec laquelle les livraisons API v1 sont signées — voir Signature — c'est donc aujourd'hui une valeur à conserver pour plus tard, pas une valeur avec laquelle vous pouvez vérifier. Si vous le perdez, supprimez le webhook et créez-en un nouveau.
Erreurs
HTTP | Code / Message | Cause |
400 |
| URL mal formée |
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 |
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 ne peut pas valider votre code de vérification. Comme toute livraison API v1, sa signature n'est pas dérivée du
secretde votre webhook — elle prouve donc la joignabilité et la forme de la payload, rien de plus. 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, il ne bénéficie pas du cache de lecture de 30 secondes, et certains chemins peuvent y être bloqués.
Exigences de l'endpoint
Votre URL webhook doit :
Répondre en 2xx au succès. Les 4xx et 3xx sont des échecs à tentative unique ; un 5xx est re-POSTé chaque minute jusqu'à ce que ça cesse (voir Comportement des retries).
Répondre dans les 10 secondes au total (5 secondes pour la connexion). Plus lent compte comme un timeout, et un timeout n'est pas re-tenté — la livraison est abandonnée.
Servir un TLS valide. Les certificats sont vérifiés strictement : un certificat auto-signé échoue sans retry.
Ne pas rediriger.
Locationn'est pas suivi ; un301/302est un échec.Accepter
POSTavecContent-Type: application/json.Lire le body raw si vous hachez quoi que ce soit (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 sha256(body) 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.