Déclencheurs web (webhooks)
Un déclencheur web lance une mission lorsqu’un service externe envoie un événement à Diadems. Il convient par exemple à une nouvelle commande, un formulaire reçu, une alerte ou un changement de statut.
Le service externe appelle uniquement l’URL publique fournie par Diadems. Il n’accède jamais directement au serveur interne, au port de la capsule ou à une clé API générale Diadems.
Créer un déclencheur
Section intitulée « Créer un déclencheur »- Ouvrez Agent → Tâches → Déclencheurs événementiels.
- Donnez un nom stable au déclencheur, par exemple
Nouvelle commande. - Rédigez la consigne que l’agent devra exécuter.
- Si nécessaire, indiquez les types d’événements acceptés, séparés par des virgules.
- Choisissez où livrer le résultat : dans le portail, sur Telegram ou vers votre application avec un callback HTTPS.
- Enregistrez.
Diadems affiche une URL HTTPS et un secret propres à ce déclencheur. Le secret sert uniquement à signer les requêtes de cette route.
Contrat recommandé pour une intégration personnalisée
Section intitulée « Contrat recommandé pour une intégration personnalisée »Une intégration générique doit utiliser la signature HMAC V2, qui lie la requête à un horodatage et empêche la réutilisation tardive d’une requête interceptée.
- Sérialisez le JSON une seule fois et conservez exactement cette chaîne comme corps HTTP.
- Produisez un timestamp Unix en secondes, par exemple
1785920400. - Construisez les octets à signer :
<timestamp>.<corps JSON brut>. - Calculez leur HMAC-SHA256 avec le secret du déclencheur.
- Encodez le résultat en hexadécimal minuscule.
- Envoyez le corps sans le sérialiser ni le modifier une seconde fois.
POST <URL affichée par Diadems>Content-Type: application/jsonX-Webhook-Timestamp: <timestamp Unix en secondes>X-Webhook-Signature-V2: <HMAC-SHA256 hexadécimal>X-Request-ID: <identifiant unique et stable de l’événement>
<corps JSON brut>La signature est donc :
hex(HMAC-SHA256(secret, UTF8(timestamp + "." + corps_json_brut)))L’horloge du service émetteur doit être synchronisée. Diadems refuse un timestamp éloigné de plus de 5 minutes de l’heure du serveur.
X-Request-ID doit identifier l’événement métier, pas la tentative HTTP. Rendez-le
unique pour tout l’agent en préfixant la source et le type, par exemple
boutique:order.created:1234. En cas de nouvelle tentative, réutilisez le même
identifiant : la capsule reconnaît durablement une livraison déjà acceptée, y
compris après un redémarrage, au lieu de relancer l’agent.
Exemple PHP
Section intitulée « Exemple PHP »La variable $body utilisée pour calculer la signature est exactement celle
transmise par cURL.
<?php
$url = getenv('DIADEMS_WEBHOOK_URL');$secret = getenv('DIADEMS_WEBHOOK_SECRET');
$eventId = 'boutique:order.created:' . $order->id;$payload = [ 'event_type' => 'order.created', 'event_id' => $eventId, 'occurred_at' => gmdate(DATE_ATOM), 'data' => [ 'order_id' => (string) $order->id, 'customer_id' => (string) $order->customer_id, ],];
$body = json_encode( $payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);$timestamp = (string) time();$signature = hash_hmac('sha256', $timestamp . '.' . $body, $secret);
$request = curl_init($url);curl_setopt_array($request, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'X-Webhook-Timestamp: ' . $timestamp, 'X-Webhook-Signature-V2: ' . $signature, 'X-Request-ID: ' . $eventId, ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10,]);
$responseBody = curl_exec($request);$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);curl_close($request);Exemple Node.js
Section intitulée « Exemple Node.js »import { createHmac } from "node:crypto";
const eventId = "boutique:order.created:1234";const body = JSON.stringify({ event_type: "order.created", event_id: eventId, 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}.`, "utf8") .update(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": eventId, }, body,});Format conseillé du JSON
Section intitulée « Format conseillé du JSON »Diadems accepte tout objet JSON, mais un format stable facilite le filtrage, la lecture et les évolutions :
{ "event_type": "order.created", "event_id": "order-1234", "occurred_at": "2026-08-05T10:15:00Z", "data": { "order_id": "1234" }}event_typedétermine le type d’événement utilisé par les filtres de la route ;event_ididentifie l’événement métier dans le contenu ;occurred_atindique quand l’événement s’est réellement produit ;datacontient seulement les données nécessaires à la mission.
Évitez d’envoyer un mot de passe, une clé API ou une donnée inutile dans le corps. Une signature authentifie l’émetteur, mais le contenu reçu reste une entrée non fiable pour l’agent.
Réponses HTTP et nouvelles tentatives
Section intitulée « Réponses HTTP et nouvelles tentatives »| Statut | Signification | Action de l’émetteur |
|---|---|---|
202 |
Événement authentifié et écrit dans la file durable de la capsule | Ne pas renvoyer |
200 |
Événement ignoré ou doublon déjà accepté | Ne pas renvoyer |
400 |
Corps illisible ou mal formé | Corriger la requête |
401 |
Signature absente ou incorrecte | Corriger le secret ou le calcul |
403 |
Route désactivée ou sans secret valide | Corriger la configuration |
404 |
Agent ou route inconnu | Corriger l’URL |
413 |
Corps supérieur à 1 Mio | Réduire le corps |
429 |
Limite temporaire atteinte | Réessayer avec attente exponentielle |
502 ou 503 |
Capsule momentanément indisponible | Réessayer avec attente exponentielle |
Pour 429, 502 et 503, conservez le même X-Request-ID. Si la nouvelle
tentative intervient plus de cinq minutes après la première, générez un nouveau
timestamp et recalculez la signature sur le même corps.
File durable et garantie de traitement
Section intitulée « File durable et garantie de traitement »Après validation de la signature, Hermes enregistre l’événement dans une file
SQLite située sur le volume privé de la capsule. La réponse 202 n’est envoyée
qu’après la validation de cette écriture. Le corps, la consigne rendue et les
informations de livraison ne sont ni enregistrés dans PostgreSQL Diadems, ni
renvoyés au plan de contrôle.
La capsule traite les événements avec une concurrence bornée. Un échec est réessayé automatiquement jusqu’à trois tentatives, avec des attentes de 30 secondes puis 5 minutes. Après le troisième échec, la livraison passe dans l’état Échec à vérifier et peut être relancée manuellement depuis la fiche du déclencheur. Le portail affiche uniquement l’identifiant, le type, l’état, les dates et le nombre de tentatives ; il ne lit jamais le contenu de l’événement.
Une fois la mission terminée, le contenu sensible de la ligne de file est effacé. La trace technique de réussite est conservée sept jours pour reconnaître les reprises tardives. Une livraison en échec conserve son contenu dans la capsule pendant quatorze jours afin de permettre une relance, puis elle est supprimée.
Réception du résultat
Section intitulée « Réception du résultat »Le webhook est asynchrone. Une réponse 202 Accepted signifie que Hermes a
authentifié et enregistré durablement la mission dans la capsule ; elle ne
contient pas le résultat final de l’agent. Ne gardez jamais la requête entrante
ouverte en attendant la fin du travail.
La fin d’un webhook reste disponible dans sa session et dans le suivi technique. Elle n’apparaît dans votre cloche et sur vos appareils que si vous activez Me notifier dans Diadems, Windows et mobile pour ce déclencheur. Ce choix est personnel : il ne crée aucune notification chez les autres membres de l’équipe.
Retour dans votre application
Section intitulée « Retour dans votre application »Choisissez Vers mon application dans Livraison du résultat pour recevoir automatiquement la réponse finale sur votre API :
- indiquez une URL HTTPS publique sans identifiants, paramètres ni fragment ;
- choisissez un jeton Bearer ou une signature HMAC ;
- saisissez le jeton ou le secret partagé ;
- adaptez si nécessaire les noms
contentetevent_id; - ajoutez les champs scalaires de l’événement à renvoyer, par exemple
data.order.idversorder_id; - créez le déclencheur puis utilisez Tester le retour.
Le secret est écrit directement dans le magasin dotenv privé de la capsule. Il n’est ni conservé dans PostgreSQL Diadems, ni réaffiché par l’API. Une rotation est possible depuis Configurer le retour : laissez le champ vide pour garder le secret existant, ou saisissez une nouvelle valeur pour le remplacer.
Le corps sortant contient uniquement les champs explicitement déclarés, la réponse finale et l’identifiant stable :
{ "order_id": "1234", "content": "Analyse terminée…", "event_id": "boutique:order.created:1234"}Avec Jeton Bearer, la capsule envoie :
Authorization: Bearer <jeton privé>X-Request-ID: <identifiant stable>Avec Signature HMAC, elle signe exactement le corps JSON avec la même construction V2 que le webhook entrant :
X-Webhook-Timestamp: <timestamp Unix>X-Webhook-Signature-V2: hex(HMAC-SHA256(secret, timestamp + "." + corps brut))X-Request-ID: <identifiant stable>Votre endpoint doit répondre avec un statut 2xx. Une redirection ou un autre
statut est considéré comme un échec. Diadems ne marque la livraison comme
terminée qu’après ce 2xx ; le callback bénéficie donc des trois tentatives, du
backoff et de la relance manuelle de la file capsule. La garantie reste « au
moins une fois » : utilisez event_id ou X-Request-ID comme contrainte unique
afin qu’une reprise n’applique pas deux fois la même mutation.
Pour empêcher un callback de devenir un accès au réseau interne, la capsule refuse les hôtes locaux, privés, réservés ou non publics, épingle l’adresse DNS publique pendant la connexion TLS et ne suit aucune redirection.
GitHub, GitLab et Svix
Section intitulée « GitHub, GitLab et Svix »Les fournisseurs pris en charge peuvent conserver leur signature native :
- GitHub :
X-Hub-Signature-256au formatsha256=<empreinte hexadécimale>; - GitLab :
X-Gitlab-Tokencontenant le secret exact ; - Svix :
svix-id,svix-timestampetsvix-signature.
Pour une intégration développée sur mesure, utilisez la signature générique V2
décrite plus haut. L’ancien en-tête X-Webhook-Signature, signé uniquement sur
le corps, reste accepté par Hermes pour compatibilité mais ne protège pas contre
le rejeu temporel et ne doit pas être utilisé pour une nouvelle intégration.
Tester et révoquer
Section intitulée « Tester et révoquer »Le bouton Tester l’événement envoie un événement signé de contrôle et lance réellement l’agent. Vérifiez ensuite le résultat dans Sessions ou sur Telegram. La fiche du déclencheur affiche également les dernières livraisons et leur état : en attente, en cours, nouvelle tentative, terminée ou en échec.
Supprimez un déclencheur qui n’est plus utilisé. Son URL et son secret cessent alors d’être valides. Pour remplacer un secret possiblement compromis, supprimez le déclencheur, recréez-le puis mettez à jour le gestionnaire de secrets du service émetteur.