Aller au contenu

Comprendre un envoi de webhook

PointValeur
MéthodePOST
En-têtes envoyésHost, User-Agent: GuzzleHttp/7, Content-Type: application/json, Content-Length. Rien d’autre : ni Accept, ni signature, ni identifiant d’événement, ni identifiant de site
CorpsJSON compact, champs de l’objet à la racine, plus une clé changed. Pas d’enveloppe, pas de métadonnée
Identification de l’événementuniquement par l’URL appelée. Déclarez une URL distincte par clé
Délais10 secondes pour établir la connexion, 30 secondes pour votre réponse
Tentatives5 tentatives au total, et seulement si la connexion échoue (DNS, refus, délai dépassé, certificat invalide) : 30 secondes d’attente entre les trois premières, puis les deux dernières enchaînées sans attente
Codes de réponsetous acceptés sans distinction. Un 500 de votre côté n’est jamais rejoué
Doublon garantisi vous répondez en plus de 30 secondes, l’envoi est considéré comme perdu et rejoué jusqu’à quatre fois, alors qu’il vous est déjà parvenu
Redirectioninterdite. Sur 301, 302 ou 303, la requête est rejouée en GET, le contenu est perdu, et Ciklik enregistre un 200 trompeur
Certificat TLSvalide et émis par une autorité reconnue. Un certificat auto-signé fait échouer l’envoi sans laisser de trace
Ordrenon garanti. Un updated_* peut vous parvenir avant le created_* du même objet
Fraîcheurle contenu est relu au moment de l’envoi, il peut être plus récent que l’événement. Seul changed décrit l’instant de l’événement
Contrat machine/openapi/ciklik-webhooks.yaml
CléObjetDélaiDétail
created_subscriptionAbonnement0 sDétail
updated_subscriptionAbonnement0 sDétail
deleted_subscriptionAbonnement0 sDétail
updated_change_choicesAbonnement0 sDétail
created_shippingboxExpédition0 sDétail
updated_shippingboxExpédition0 sDétail
deleted_shippingboxExpédition0 sDétail
created_checkoutorderCommande10 sDétail
updated_checkoutorderCommande0 sDétail
created_checkouttransactionTransaction10 sDétail
updated_checkouttransactionTransaction10 sDétail
created_checkoutinvoiceFacture15 sDétail
created_userClient0 sDétail
updated_userClient0 sDétail
created_addressAdresse0 sDétail
updated_addressAdresse0 sDétail
created_optinInscription marketing10 sDétail
updated_optinInscription marketing0 sDétail

updated_change_choices n’appartient pas aux 17 clés de l’API : elle n’est activable que par l’équipe Ciklik, voir Configurer les webhooks par l’API.

  • Pas de signature ni de secret partagé. L’URL de réception est le seul secret du dispositif.
  • Pas de nom d’événement dans le contenu. Rien ne dit qu’il s’agit d’un created_subscription plutôt que d’un updated_user.
  • Pas d’identifiant d’envoi ni d’horodatage d’événement, donc aucune clé d’idempotence fournie.
  • Aucun accusé de réception attendu. Le code de réponse renvoyé n’est pas analysé.

Le corps est constitué des champs de l’objet concerné, à la racine, plus une clé changed. L’objet dépend de la clé d’événement : il est décrit champ par champ sur la page de référence correspondante (tableau ci-dessus). Voici le plus simple du catalogue, un created_optin, quatre champs et changed :

created_optin.json
{
"email": "claire.martin@example.com",
"tenant_id": 1,
"valid": true,
"list_id": "9f2c4e7a1b",
"changed": []
}

changed prend trois formes selon le type d’événement.

Un tableau JSON vide, pas un objet vide :

{
"changed": []
}

Cinq règles à connaître avant d’exploiter changed :

  • Ce sont des valeurs brutes de la base, pas les valeurs telles que l’API les renvoie ailleurs : dates au format Y-m-d H:i:s en UTC sans fuseau, colonnes JSON sous forme de chaîne encodée, décimaux en chaîne.
  • updated_at figure dans changed sur presque toutes les modifications ; il n’est absent que si deux écritures tombent dans la même seconde.
  • Les noms de colonnes internes peuvent différer des noms de champs exposés ailleurs dans le contenu.
  • Un envoi peut contenir des champs que vous n’avez pas modifiés vous-même, parce qu’un traitement interne a touché le même enregistrement au même moment.
  • Ne faites jamais dépendre une décision métier de la seule présence d’une clé dans changed : vérifiez aussi sa valeur.
TypeFormatExemple
DatesISO 8601 UTC avec microsecondes et suffixe Z ; T00:00:00.000000Z pour une date sans heure2026-09-10T08:15:42.000000Z
Montants de commandechaîne, virgule décimale (total_tax_paid, total_discount_inc, total_paid, total_shipping_paid)"31,81"
Autres montantschaîne, point décimal (revenue, customerRevenue, shipping)"31.81"
Prix de lignenombre flottant (items[].price)29.9
Compteurs de l’objet expéditionchaîne, alors que la valeur est numérique (subscriptions_count, shipped_count, orderable_id à la racine de l’expédition ; le shipped_count d’une formule ou d’un transporteur reste un entier)"3"

Quatre endroits du contenu ne sont pas un contrat de champs et ne doivent pas être exploités au-delà de ce qui suit :

  • items[].orderable : reflet interne du produit ou de la formule, non contractuel. N’exploitez que id, name, price et ref.
  • transporter.relayOptions : contenu variable selon le transporteur, non contractuel, à ignorer.
  • invoice.customer : instantané des coordonnées client à l’émission de la facture, structure libre.
  • invoice.company : instantané des coordonnées de votre société à l’émission de la facture, structure libre.

Le délai dépend de la clé : 0, 10 ou 15 secondes (tableau des 18 clés). Ce n’est pas une garantie de fraîcheur, c’est l’inverse : le contenu est relu au moment où l’envoi part, pas au moment de l’événement. Entre les deux, l’objet a pu être modifié à nouveau. Seul changed décrit fidèlement l’instant de l’événement ; le reste du corps reflète l’état le plus récent connu au moment de l’envoi.

Aucun ordre n’est garanti : délais différents selon la clé et plusieurs envois pouvant être traités en parallèle, si bien qu’un updated_* peut arriver avant le created_* du même objet. Les doublons sont fréquents et normaux : écritures successives sur un même objet, cascades entre abonnement, expéditions et commandes, ou réponse trop lente qui déclenche un nouvel envoi alors que le premier est bien arrivé. Traitez ces cas comme la norme, voir Bonnes pratiques et dépannage pour la parade.

Étape par étape :

événement dans votre boutique (création, modification ou suppression)
-> résolution de l'URL déclarée (mise en cache 60 s)
-> mise en file, délai de 0, 10 ou 15 s selon la clé
-> POST vers votre URL de réception
connexion : 10 s max, lecture de votre réponse : 30 s max
-> échec de connexion (DNS, refus, délai, certificat) ?
oui -> nouvelle tentative, jusqu'à 5 au total : 30 s entre les trois premières, puis les deux dernières enchaînées sans attente
non -> code de réponse journalisé tel quel, aucune nouvelle tentative
-> ligne journalisée dans la trace conservée 20 jours (sauf échec de connexion)
  • Le mécanisme et le contenu des envois sont exactement les mêmes que pour une boutique Ciklik classique.
  • Les abonnements portent en plus le champ external_fingerprint, et les commandes le champ prestashop_order_id.
  • Le passage d’une commande en demande de rétractation n’est jamais notifié : c’est volontaire côté PrestaShop.
  • Le module PrestaShop dispose de son propre canal de synchronisation, distinct de ces webhooks marchands. Ne les confondez pas.
  • Un jeton d’API est créé automatiquement pour les sites PrestaShop, voir Configurer les webhooks par l’API.

Voir aussi la rubrique PrestaShop.

Le fichier /openapi/ciklik-webhooks.yaml décrit les 18 clés, les trois opérations de configuration et un exemple réel par clé. Chargez-le dans un client d’API, générez-en des types, ou donnez-le à un assistant de code pour qu’il écrive votre endpoint de réception.