Passer au contenu principal

Enregistrer un webhook

Créer, lister, tester et supprimer des abonnements webhook pour votre boutique.

Écrit par Support

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 event dans 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

url

string (URL https)

Doit être http:// ou https://. En prod : toujours https://. La longueur maximale est de 500 caractères — une URL plus longue est rejetée en 500 server_error, pas en erreur de validation.

events

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

bad_request "url must be a valid http(s) URL"

URL mal formée

400

bad_request "events must be a non-empty list"

Tableau vide

400

bad_request "unknown event: foo. Allowed: …"

Nom d'event hors catalogue

400

bad_request "Idempotency-Key header is required for write requests"

Idempotency-Key manquant sur un POST/DELETE

400

bad_request "Idempotency-Key must be <=64 chars, [A-Za-z0-9_-:.]"

Clé trop longue, ou contenant des caractères hors de cet ensemble (les +, /, = du base64 sont tous rejetés)

403

forbidden "API is in pilot mode; key not enrolled"

La clé existe mais n'est pas inscrite au pilote

500

server_error "Could not register webhook"

Généralement une url de plus de 500 caractères

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

active

Reçoit les livraisons. En pratique, c'est la seule valeur que vous verrez.

paused

Réservé pour un usage futur, non utilisé actuellement — et il n'existe pas de page dashboard pour les webhooks API v1.

dead

Réservé pour un usage futur, non utilisé actuellement. Il n'y a pas de désactivation automatique ; failure_count se contente de compter les tentatives échouées et retombe à 0 au succès suivant.

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 secret de 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-17 dans 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. Location n'est pas suivi ; un 301/302 est un échec.

  • Accepter POST avec Content-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_id PEUT 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, puis JSON.parse(req.body) dans le handler.

  • Django : request.body est 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.

Avez-vous trouvé la réponse à votre question ?