Webhooks
Un webhook envoie les Business Events de votre boutique à un endpoint de votre propre serveur au moment où ils se produisent : une commande passée, confirmée, remise à un transporteur, livrée. Chaque événement est envoyé en POST au format JSON, signé selon la spécification Standard Webhooks, et renvoyé jusqu'à ce que votre endpoint l'accepte.
Les transporteurs qui signalent des livraisons à votre boutique, c'est l'autre sens : lisez Webhooks des transporteurs.
Ajouter un webhook
Dans le tableau de bord, ouvrez Intégrations dans le menu latéral et choisissez Ajouter un webhook ; donnez-lui un nom, l'URL de votre endpoint et les événements qu'il reçoit. Par l'API, POST /api/event-consumers avec le scope integrations:manage (integrations:read liste les webhooks et leurs livraisons) :
Code
La réponse contient le webhook et son secret de signature, whsec_…. Il n'est affiché qu'une fois : gardez-le dans les secrets de votre serveur. Toutes les réponses suivantes l'affichent masqué.
- L'URL doit être en
https, publique sur Internet (aucune adresse privée, locale ou réservée), sans nom d'utilisateur ni mot de passe. - Une boutique a au plus 10 webhooks.
- Un webhook reçoit les événements enregistrés après son ajout. Pour lui envoyer des événements plus anciens, utilisez un Replay (voir plus bas).
Un abonnement nomme des types d'événements exactement (order.confirmed), par sujet (order.*, shipment.*), ou tous (*). Un sujet ou * prend aussi les types ajoutés plus tard au catalogue.
Ce que reçoit votre endpoint
Chaque livraison est un POST vers votre URL :
| En-tête | Valeur |
|---|---|
content-type | application/json |
user-agent | CodFlow-Webhooks/1 |
webhook-id | L'id de l'événement. Le même à chaque tentative et à chaque Replay. |
webhook-timestamp | L'heure d'envoi de cette tentative, en secondes Unix. |
webhook-signature | v1,<signature>, une par secret de signature, séparées par des espaces. |
Le corps est le Business Event, exactement tel que le renvoie l'API du journal d'événements :
Code
| Champ | Sens |
|---|---|
id | L'id unique de l'événement. Reconnaissez par lui un événement déjà traité. |
sequence | Sa position dans le journal d'événements de la boutique : un événement plus récent a un numéro plus grand. |
type, version | Le type d'événement et la version de ses data. |
subject | Ce dont parle l'événement : une commande ou une expédition. |
orderId | La commande concernée. |
occurredAt | Quand il s'est produit : un transporteur peut signaler une livraison des heures plus tard. |
recordedAt | Quand la boutique l'a enregistré. |
actor, origin | Qui l'a causé (équipe, agent IA, client, transporteur, système) et par où (tableau de bord, API, vitrine, webhook transporteur…). |
data | La commande, et l'expédition pour un événement de livraison, telles qu'elles étaient à ce moment-là. |
Les montants sont des chaînes décimales avec leur devise : { "amount": "9000.00", "currency": "DZD" }. Un événement ne dépasse jamais 64 Ko.
Vérifier la signature
Vérifiez chaque requête avant d'agir. La signature est le HMAC-SHA256 en base64 de <webhook-id>.<webhook-timestamp>.<corps brut>, avec pour clé les octets que code le secret de signature : la partie après whsec_, décodée en base64.
- Lisez le corps brut, tel qu'il est arrivé. Analyser puis réécrire le JSON change les octets, et la signature ne correspond plus.
- Refusez un
webhook-timestampà plus de cinq minutes de votre horloge, pour qu'une requête interceptée ne puisse pas être rejouée plus tard. - Acceptez la requête quand l'une des signatures
v1,séparées par des espaces correspond, comparée en temps constant.
Node.js
Code
Passez le corps brut sous forme de chaîne, par exemple avec express.text({ type: "application/json" }).
PHP
Code
Le corps brut est file_get_contents('php://input') (dans Laravel, $request->getContent()) ; donnez les en-têtes avec des noms en minuscules.
Python
Code
Le corps brut est request.get_data() avec Flask, ou await request.body() avec FastAPI.
Toute bibliothèque Standard Webhooks vérifie aussi ces requêtes, par exemple new Webhook(secret).verify(rawBody, headers) avec le paquet standardwebhooks.
Changer le secret de signature
Changer le secret de signature dans le tableau de bord, ou POST /api/event-consumers/{id}/rotate-secret, renvoie une seule fois un nouveau secret. Pendant 24 heures, chaque requête porte une signature par secret : passez votre endpoint au nouveau dans ce délai, et rien n'est refusé. Ensuite, seul le nouveau secret signe.
Répondre vite
Votre réponse décide de la suite :
| Votre endpoint répond | Ce que fait la boutique |
|---|---|
Tout 2xx, en moins de 15 secondes | Livré. |
410 Gone | Arrête ce webhook et ne lui envoie plus rien tant que vous ne le redémarrez pas. |
| Autre chose : un autre statut, une redirection, pas de réponse à temps, pas de connexion | Réessaie plus tard, selon le calendrier ci-dessous. |
Répondez 2xx dès que l'événement est stocké, puis faites le travail. Les redirections ne sont pas suivies : donnez l'URL finale. Un en-tête Retry-After sur un refus est respecté, jusqu'à un jour.
Nouvelles tentatives, doublons et ordre
Une livraison est tentée 8 fois. La première tentative part quelques secondes après l'événement ; chaque échec attend avant la suivante :
| Tentative | Attente après la précédente |
|---|---|
| 1 | aucune : quelques secondes après l'événement |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 heures |
| 5 | 6 heures |
| 6 | 12 heures |
| 7 | 24 heures |
| 8 | 24 heures |
Après le dernier échec, la livraison est abandonnée : elle reste dans le tableau de bord, où vous pouvez la réessayer. Les nouvelles tentatives tournent toutes les cinq minutes, donc une attente peut durer jusqu'à cinq minutes de plus. Un webhook dont toutes les tentatives échouent pendant trois jours est arrêté ; redémarrez-le depuis le tableau de bord une fois votre endpoint réparé.
Les livraisons sont au moins une fois et sans ordre garanti :
- Vous pouvez recevoir un événement deux fois. Gardez le
webhook-idde ce que vous avez traité et ignorez-le la deuxième fois. - Deux événements peuvent arriver dans le désordre. Ordonnez-les par
sequence, ou paroccurredAtpour l'heure à laquelle ils se sont produits.
Livrer à nouveau
Depuis Intégrations → Envois dans le tableau de bord, ou par l'API :
- Réessayer une livraison :
POST /api/event-deliveries/{id}/retry. - Renvoyer… une plage d'événements :
POST /api/event-consumers/{id}/replayavecfromSequenceettoSequence, au plus 1 000 événements à la fois. Les événements enregistrés pendant que le webhook était arrêté sont inclus.
Un événement livré à nouveau garde son id, donc votre endpoint le reconnaît.
Événements de test
Envoyer un test dans le tableau de bord, ou POST /api/event-consumers/{id}/test avec un type facultatif, envoie tout de suite à votre endpoint un exemple d'un type d'événement, signé comme les autres, et montre ce que votre endpoint a répondu. Un événement de test a la sequence 0 et un id qui commence par test_ ; il n'est pas dans votre journal d'événements.
Types d'événements
| Type d'événement | Quand |
|---|---|
order.created | Une commande a été passée : sur la boutique, dans le tableau de bord, par l'API ou par un agent IA. |
order.updated | Le client, la destination ou les notes d'une commande ont été modifiés. |
order.confirmed | Le client a confirmé la commande, le plus souvent au téléphone. |
order.unreachable | Le client n'a pas pu être joint pour confirmer la commande. |
order.preparing | La commande est en préparation. |
order.ready | La commande est emballée, prête à être remise à la livraison. |
order.cancelled | La commande a été annulée. |
order.line_returned | Des unités d'une ligne de commande ont été refusées à la porte et sont revenues. |
order.deleted | La commande a été supprimée ; l'événement la contient telle qu'elle était. |
shipment.handed_off | La commande a été remise à un transporteur ou à un livreur de la boutique. |
shipment.out_for_delivery | Le colis est parti chez le client. |
shipment.delivery_failed | Une tentative de livraison du colis a échoué. |
shipment.delivered | Le colis a été livré et payé à la livraison. |
shipment.return_started | Le colis revient vers la boutique. |
shipment.returned | Le colis est de retour à la boutique. |
shipment.cancelled | L'expédition a été annulée avant la livraison. |
shipment.voided | La remise a été annulée : la commande n'est plus en livraison. |
Le schéma complet de chaque type est dans la référence de l'API, sous Webhooks.
Voir aussi
- Authentification pour les clés d'API et les scopes
integrations:readetintegrations:manage - Erreurs pour l'enveloppe d'erreur
- Webhooks des transporteurs pour les livraisons signalées par vos transporteurs