Aller au contenu

Déclencheurs web (webhooks)

Un déclencheur web lance l’agent lorsqu’un service externe envoie un événement à Diadems : nouvelle commande, formulaire reçu, alerte ou changement de statut.

Diadems relaie la requête vers le webhook natif de la capsule. La signature, le filtrage des événements, le déclenchement et la livraison sont gérés par Hermes ; Diadems ne maintient pas de second moteur de file ou de reprise.

  1. Ouvrez Agent → Tâches → Webhooks ou demandez à l’agent de créer la connexion avec sa compétence Webhooks Diadems.
  2. Donnez un nom stable au déclencheur, par exemple nouvelle-commande.
  3. Rédigez la consigne que l’agent devra exécuter.
  4. Indiquez les types d’événements acceptés.
  5. Choisissez la destination : portail, Telegram, e-mail ou retour vers votre application.
  6. Enregistrez et testez avec un événement non destructif.

Créez une route distincte lorsque la consigne, la destination, les champs du retour ou les permissions diffèrent. Par exemple, une commande à analyser et une question posée par un employé ne doivent pas partager une route simplement parce qu’elles viennent du même logiciel.

Diadems affiche une URL HTTPS et un secret propres à la route. Le service émetteur doit signer le corps JSON avec ce secret.

Chaque webhook dispose du réglage Enregistrer automatiquement en mémoire dans sa configuration. Sur les agents compatibles, il est désactivé par défaut, y compris pour les webhooks existants sans choix enregistré. Activez-le pour les événements apportant une décision, un engagement ou un changement durable. Les notifications répétitives et les contrôles sans nouveauté peuvent rester sans sauvegarde automatique.

Le choix d’un webhook ne modifie pas les autres. Le résultat reste dans l’historique, les souvenirs existants sont conservés et l’agent peut encore les consulter ou répondre à une demande explicite de mémorisation. Si le moteur doit être mis à jour, le formulaire le précise ; ce réglage n’est pas encore appliqué sur cet agent.

Faire préparer l’intégration par un agent développeur

Section intitulée « Faire préparer l’intégration par un agent développeur »

Lorsque votre application est maintenue avec l’aide d’un agent développeur, commencez généralement par lui demander d’analyser l’événement métier. Il connaît le code, le moment exact où l’événement devient définitif et les données réellement disponibles. Il prépare le contrat ; votre agent Diadems configure ensuite la réception sans avoir besoin d’accéder au dépôt de l’application.

Transmettez cette page à votre agent développeur avec une demande simple :

Je veux connecter cette application à mon agent Diadems lorsqu’un nouvel
événement <décrivez l’événement> se produit.
Analyse le code et prépare le dossier de raccordement Diadems décrit dans la
documentation. Ne programme pas encore l’envoi : je te transmettrai ensuite
l’URL et le secret HMAC créés par mon agent Diadems.

L’agent développeur doit remettre un dossier de raccordement contenant :

  1. le nom et la finalité de l’événement ;
  2. une valeur stable pour event_type, par exemple order.created ;
  3. le point précis du code où l’événement doit être envoyé, uniquement après la validation métier ou la transaction concernée ;
  4. un exemple réaliste du corps JSON avec un event_id unique et stable ;
  5. la liste des champs obligatoires, facultatifs et des données sensibles à exclure ;
  6. la stratégie d’idempotence, de réessai, de délai maximal et de journalisation ;
  7. les fichiers ou modules qui devront être modifiés ;
  8. le résultat attendu de l’agent et, si nécessaire, les informations permettant de rapprocher une réponse asynchrone de l’événement initial.

Le dossier peut suivre ce modèle :

# Dossier de raccordement Diadems
- Application : <nom>
- Événement : <description métier>
- event_type : <type stable>
- Déclenchement dans le code : <fichier, fonction et moment>
- Identifiant idempotent : <origine de event_id>
- Résultat attendu : <action ou analyse demandée à l’agent>
- Retour applicatif : <non, ou besoin et identifiants de corrélation>
## Exemple de payload
{
"event_type": "<type stable>",
"event_id": "<identifiant unique>",
"occurred_at": "<date ISO 8601 facultative>",
"data": {}
}
## Champs
- Obligatoires :
- Facultatifs :
- Interdits ou sensibles :
## Livraison
- Réessais : erreurs réseau, 429 et 5xx avec attente exponentielle et aléa
- Idempotence : même event_id et même X-Request-ID pour un même événement
- Délai maximal :
- Journaux autorisés, sans payload sensible :
## Modification prévue
- Fichiers ou modules :
- Tests à ajouter :
- Retour arrière :

Donnez ensuite ce dossier à votre agent Diadems :

Crée le déclencheur web correspondant à ce dossier de raccordement, configure
une consigne sûre et teste-le. Remets-moi ensuite le nécessaire à transmettre
à mon agent développeur pour terminer le branchement.

Votre agent Diadems doit vous remettre un kit de branchement comprenant le nom de la route, l’URL publique, le secret HMAC nouvellement créé, les en-têtes attendus, les types d’événements acceptés et le résultat du test. Le secret n’est affiché qu’à sa création : transmettez-le par un canal confidentiel et faites-le enregistrer comme variable d’environnement ou dans le gestionnaire de secrets de l’application.

Redonnez enfin ce kit à l’agent développeur. Il peut alors programmer l’envoi, ajouter les tests et déclencher un événement non destructif. L’intégration n’est terminée qu’après vérification de la réception dans Diadems et d’un nouvel envoi avec le même X-Request-ID, qui ne doit produire aucun traitement métier en double.

Le corps doit être un objet JSON. Pour une intégration générique Diadems, utilisez au minimum cette enveloppe :

{
"event_type": "order.created",
"event_id": "backice:order.created:1234:v1",
"occurred_at": "2026-08-05T10:00:00Z",
"data": {
"order_id": "1234"
}
}
Champ Statut Rôle
event_type Obligatoire Type stable utilisé par la route pour accepter ou ignorer l’événement.
event_id Obligatoire Identifie cette occurrence métier. Utilisez la même valeur dans X-Request-ID.
occurred_at Facultatif Date ISO 8601 de l’événement métier. Elle est transmise à l’agent, mais Diadems ne l’utilise pas comme limite de fraîcheur.
data Recommandé Objet contenant uniquement les données nécessaires au traitement.

L’enveloppe est ouverte : des champs supplémentaires à la racine sont acceptés. Ils sont disponibles pour la consigne, mais une route bien configurée ne doit référencer que ceux dont elle a besoin. Les champs obligatoires propres au métier sont vérifiés par la consigne de la route ; Diadems ne devine pas leur sens.

event_id identifie une occurrence, pas seulement une ressource. Si une commande doit être analysée une seconde fois volontairement, utilisez un nouvel identifiant d’occurrence, par exemple backice:order.analysis:1234:2. Ne réutilisez jamais un identifiant pour un corps différent.

Le corps utilisé pour calculer la signature doit être strictement identique au corps transmis.

POST <URL affichée par Diadems>
Content-Type: application/json
X-Webhook-Timestamp: <timestamp Unix en secondes>
X-Webhook-Signature-V2: <HMAC-SHA256 hexadécimal de timestamp + "." + corps brut>
X-Request-ID: <event_id>
<corps JSON brut>

Exemple Node.js :

import { createHmac } from "node:crypto";
const body = JSON.stringify({
event_type: "order.created",
event_id: "order-1234",
occurred_at: new Date().toISOString(),
data: { order_id: "1234" },
});
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = createHmac("sha256", process.env.DIADEMS_WEBHOOK_SECRET)
.update(`${timestamp}.${body}`, "utf8")
.digest("hex");
const response = await fetch(process.env.DIADEMS_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Webhook-Timestamp": timestamp,
"X-Webhook-Signature-V2": signature,
"X-Request-ID": "order-1234",
},
body,
});
if (response.status === 429 || response.status >= 500) {
// Replacer le même événement dans la file locale avec attente exponentielle.
}

Le timestamp doit se trouver dans une fenêtre de cinq minutes autour de l’horloge du serveur. Pour réessayer le même événement, conservez exactement le même X-Request-ID et le même corps ; recalculez la signature avec un nouveau timestamp uniquement si l’ancien a expiré.

Une réponse 202 Accepted confirme la réception et le lancement asynchrone du traitement, pas sa réussite finale. Le service émetteur doit conserver une file ou un journal local durable s’il doit pouvoir réconcilier les événements dont il n’a pas reçu le résultat final.

Ce vecteur est volontairement figé. Le corps est une seule ligne UTF-8, sans espace final ni retour à la ligne.

Secret : test-secret
Timestamp : 1785920400
Corps brut : {"event_type":"order.created","event_id":"order-1234","occurred_at":"2026-08-05T10:00:00Z","data":{"order_id":"1234"}}
Données signées : 1785920400.{"event_type":"order.created","event_id":"order-1234","occurred_at":"2026-08-05T10:00:00Z","data":{"order_id":"1234"}}
Signature hexadécimale attendue : 64ac9877128813c8e1c57b266da2fa2c38d6940606cad92cc7d770bc0c156b8d

Si votre bibliothèque ne produit pas exactement cette signature, ne branchez pas encore le service réel : vérifiez l’encodage UTF-8, l’ordre des propriétés, les espaces et les retours à la ligne.

Le corps JSON de la réponse doit être lu même lorsque le statut vaut 200 : ignored et duplicate sont des résultats normaux, pas des pannes.

HTTP Signification Réessayer ?
200 avec status: ignored Type d’événement non accepté ou filtre non satisfait. Aucun agent n’est lancé. Non. Corriger la route ou l’événement si ce résultat est inattendu.
200 avec status: duplicate Même identifiant déjà reçu pendant la fenêtre de déduplication. Non. Le premier envoi reste la référence.
202 avec status: accepted Événement reçu et traitement asynchrone lancé. Non, sauf absence de résultat détectée par une procédure de réconciliation.
400 Corps illisible ou JSON invalide. Non, corriger la requête.
401 Signature absente, invalide, timestamp absent ou hors fenêtre. Non avec les mêmes paramètres ; corriger l’horloge, le secret ou la signature.
403 Route désactivée ou configuration de sécurité incomplète. Non, corriger la configuration.
404 URL, agent, profil ou route inconnu ou supprimé. Non, sauf pendant une migration explicitement prévue.
413 Corps supérieur à 1 Mo. Non, réduire le payload ou transmettre une référence sûre.
429 Limite de débit atteinte. L’en-tête Retry-After indique le nombre de secondes à attendre. Oui, après Retry-After, puis avec attente exponentielle et aléa.
502 ou 503 Relais ou capsule temporairement indisponible. Oui, avec attente exponentielle et aléa.
autre 5xx Erreur temporaire non prévue. Oui, avec un nombre de tentatives borné.

Chaque route Diadems accepte 30 requêtes par minute. Cette valeur est fixe dans la version actuelle du cockpit : elle n’est pas configurable route par route. Toute réponse 429 publique contient Retry-After: 60. Pour un import ou une resynchronisation, placez les événements dans une file côté application et lissez le débit au lieu d’envoyer une rafale.

Hermes conserve les X-Request-ID vus dans un cache d’une heure. Un doublon dans cette fenêtre reçoit 200 avec status: duplicate et ne relance pas l’agent. Cette protection de transport ne remplace pas une idempotence métier durable : le service émetteur et le destinataire d’un callback doivent enregistrer les événements déjà appliqués. Ils doivent notamment rester sûrs après un redémarrage ou un rejeu effectué plus d’une heure après le premier envoi.

La destination Retour applicatif ferme la boucle de manière asynchrone. Lorsque l’agent termine, Diadems effectue un POST vers l’URL HTTPS publique configurée sur la route. Les redirections, les adresses locales et les adresses privées sont refusées.

La route définit :

  • l’URL HTTPS du callback ;
  • la méthode d’authentification : HMAC SHA-256, Bearer ou aucune ;
  • le nom d’une variable secrète déjà enregistrée dans le coffre de l’agent ;
  • le nom du champ contenant la réponse, généralement content ;
  • jusqu’à 16 champs de corrélation recopiés depuis l’événement, dont event_id obligatoirement.

Exemple de corps reçu :

{
"event_id": "order-1234",
"order_id": "1234",
"content": "La commande a été analysée…"
}

Les en-têtes communs sont :

Content-Type: application/json
Idempotency-Key: order-1234
X-Request-ID: order-1234
User-Agent: Hermes-HTTP-Callback/1.0

Avec l’authentification Bearer, Diadems ajoute Authorization: Bearer <secret>. Avec HMAC SHA-256, Diadems ajoute X-Webhook-Timestamp et X-Webhook-Signature-V2 en signant exactement timestamp + "." + corps brut, comme pour le webhook entrant.

Le callback accepte tout statut 2xx comme une réussite. Après que l’agent a produit sa réponse finale, Diadems effectue au maximum 7 tentatives dans une fenêtre totale de 5 minutes. Chaque requête expire après 30 secondes. Les erreurs réseau, 429 et 5xx sont réessayées ; les autres 4xx sont définitives. L’attente progresse approximativement selon 1 s, 4 s, 15 s, 45 s, 90 s, 120 s, avec un léger aléa. Sur un 429, Retry-After est respecté jusqu’à 120 secondes, sans dépasser la fenêtre totale.

Le récepteur doit traiter event_id ou Idempotency-Key de façon durable et répondre 2xx uniquement après avoir enregistré le résultat de manière sûre. Le corps de réponse du callback n’est pas utilisé.

Livraison asynchrone, pas une file garantie

Hermes v0.21.0 n’expose pas de GET public permettant d’interroger l’état d’un événement par event_id. Diadems ne simule pas cet endpoint et ne maintient pas une seconde file à côté d’Hermes. Le callback est donc une livraison bornée et non une garantie de remise après redémarrage de la capsule.

Si le résultat est indispensable, l’application émettrice doit conserver une ligne locale pending après le 202 accepted, la passer à completed lors du callback idempotent, puis la placer en needs_review après son propre délai métier. La réconciliation se fait alors avec la session webhook visible dans le cockpit. N’émettez pas automatiquement un nouvel event_id : le premier run peut encore terminer et produire un second effet. Une relance doit être explicite, idempotente côté métier et effectuée après vérification.

Une route existante conserve son secret pendant une modification. Pour le faire tourner sans perdre d’événement :

  1. créez une nouvelle route avec un nouveau nom et un nouveau secret ;
  2. configurez l’émetteur pour envoyer temporairement vers les deux routes, ou basculez-le avec une file de reprise ;
  3. validez un événement réel et un doublon sur la nouvelle route ;
  4. arrêtez l’ancienne destination ;
  5. supprimez l’ancienne route après vidage des événements en vol.

Une route supprimée invalide immédiatement son URL et son secret. Elle ne se renomme pas en place.

  • envoyez uniquement les données nécessaires à la mission ;
  • n’envoyez jamais de mot de passe ou de clé API dans le corps ;
  • utilisez le même event_id dans le JSON et X-Request-ID ;
  • journalisez le statut HTTP, status, event_id, le numéro de tentative et la durée, jamais le secret ni le payload sensible ;
  • conservez localement l’état pending, completed ou needs_review des résultats indispensables ;
  • n’automatisez jamais une relance avec un nouvel event_id sans vérification métier préalable ;
  • consultez le résultat dans Sessions ou dans la destination choisie.

Le bouton Tester l’événement envoie un événement de contrôle signé. Supprimez un déclencheur inutilisé ; son URL et son secret cessent alors d’être valides.

Tâches → Webhooks ouvre la liste. La création et la modification s’effectuent dans une page dédiée ; le fil d’Ariane revient aux déclencheurs. Les choix de livraison et d’authentification s’ouvrent eux aussi dans une sous-vue. Le bouton Demander à [nom de l’agent] de Tâches ouvre une nouvelle conversation et envoie votre demande d’aide. Indiquez que votre demande concerne un webhook et transmettez ce guide : l’agent peut gérer ses souscriptions natives avec sa compétence Webhooks Diadems.