Webhooks sortants
Avec un webhook sortant, exoCortex envoie une requête POST à votre système chaque fois que se produit un événement auquel vous êtes abonné : un ticket créé, un changement de statut, un SLA sur le point d’expirer. Votre système réagit ainsi immédiatement, sans interroger l’API à intervalles réguliers.
Créer un abonnement
Section intitulée « Créer un abonnement »- Dans exoCortex Core, ouvrez Integrations → Webhooks. Un rôle administratif est nécessaire.
- Indiquez l’URL qui recevra les événements. Elle doit commencer par
https://. - Choisissez les événements.
*signifie tous les événements. - Choisissez le format :
json: le format standard d’exoCortex, décrit sur cette page ;datadog,pagerduty,slack-block: le format natif de ces services, à utiliser lorsque l’URL est la leur.
- Si votre passerelle l’exige, ajoutez des en-têtes fixes, par exemple un en-tête d’authentification.
- Enregistrez. Si vous n’avez pas indiqué de secret de signature, exoCortex en génère un (
whsec_…) et ne l’affiche qu’une seule fois : enregistrez-le, il vous sert à vérifier les signatures.
Avec l’action de test, vous envoyez à l’abonnement l’événement webhook.test et vous vérifiez que votre système le reçoit.
Événements
Section intitulée « Événements »| Événement | Quand il arrive |
|---|---|
ticket.created |
Un ticket a été créé |
ticket.comment_added |
Un commentaire a été ajouté |
ticket.field_changed |
Un champ a changé |
ticket.assignee_changed |
L’assigné a changé |
ticket.status_changed |
Le statut a changé |
ticket.priority_changed |
La priorité a changé |
ticket.sla_warning |
Un SLA est sur le point d’expirer |
ticket.sla_breach |
Un SLA a expiré |
ticket.resolved |
Le ticket a été résolu |
ticket.closed |
Le ticket a été fermé |
ticket.reopened |
Le ticket a été rouvert |
ticket.link_created |
Un lien entre tickets a été créé |
ticket.link_deleted |
Un lien entre tickets a été supprimé |
routing.no_match |
Aucune règle de routage n’a trouvé de file pour le ticket |
routing.manual_routed |
Un opérateur a acheminé le ticket manuellement |
webhook.test |
Vous avez envoyé un événement de test à l’abonnement |
En plus de ceux-ci, vous pouvez vous abonner par leur nom aux événements des règles de notification de votre tenant, par exemple approval_requested.
exoCortex peut ajouter de nouveaux types d’événements : votre système doit ignorer ceux qu’il ne connaît pas.
Format de l’événement
Section intitulée « Format de l’événement »Chaque événement au format json a la même structure :
{ "id": "evt_8a1f4c2e9b7d4e3f8a6b5c4d3e2f1a00", "type": "ticket.status_changed", "created_at": "2026-11-01T09:31:47.120Z", "api_version": "1", "tenant_id": "ACME", "data": { "ticket": { "key": "ACME-1042", "summary": "Payment gateway returns HTTP 502", "status": "In Progress", "priority": "High", "issuetype": "IT-INC", "assignee": "jane.doe", "created": "2026-11-01T09:14:05.000Z", "updated": "2026-11-01T09:31:47.000Z" }, "actor": "jane.doe", "changes": { "from": "Open", "to": "In Progress" } }}ididentifie l’événement et il est identique pour tous les abonnements qui le reçoivent.actorest l’utilisateur qui a déclenché l’événement, ousystem.changescontient les détails de l’événement : par exemple l’ancien et le nouveau statut, ou le texte du commentaire.
Jusqu’au 30 juin 2027, le corps contient aussi les champs dépréciés event, timestamp et source, ainsi qu’une copie des champs de data au premier niveau. Un nouveau système doit lire uniquement les champs décrits ci-dessus.
En-têtes
Section intitulée « En-têtes »| En-tête | Exemple | Signification |
|---|---|---|
X-Exo-Timestamp |
1793491200 |
Moment de la signature, en secondes Unix |
X-Exo-Signature |
v1=5257a869… |
Signature de l’événement |
X-Exo-Event |
ticket.created |
Type d’événement, identique à type |
X-Exo-Delivery |
dlv_0b9d… |
Identifiant unique de la tentative de livraison |
Vérifier la signature
Section intitulée « Vérifier la signature »Avant de faire confiance à un événement, vérifiez sa signature :
- Lisez le corps tel qu’il est arrivé, sans interpréter ni réécrire le JSON : les octets changeraient.
- Rejetez la requête si
X-Exo-Timestampest absent ou s’écarte de plus de 300 secondes de votre horloge. Vous bloquez ainsi les requêtes rejouées. Gardez votre horloge synchronisée par NTP. - Calculez le HMAC-SHA256 de
timestamp + "." + bodyavec le secret de l’abonnement, en hexadécimal minuscule. - Comparez le résultat, en temps constant, avec chaque valeur
v1=deX-Exo-Signature. Pendant la rotation du secret, l’en-tête peut en contenir plusieurs, séparées par des virgules.
Exemple en Node.js :
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyExoWebhook(rawBody, headers, secret, toleranceSec = 300) { const ts = headers['x-exo-timestamp']; const sigHeader = headers['x-exo-signature'] || ''; if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSec) return false; const expected = Buffer.from(createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex')); return sigHeader .split(',') .map((s) => s.trim()) .filter((s) => s.startsWith('v1=')) .some((s) => { const got = Buffer.from(s.slice(3)); return got.length === expected.length && timingSafeEqual(got, expected); });}Exemple en Python :
import hashlib, hmac, time
def verify_exo_webhook(raw_body: bytes, headers, secret: str, tolerance_sec: int = 300) -> bool: ts = headers.get('X-Exo-Timestamp') if not ts or abs(time.time() - int(ts)) > tolerance_sec: return False expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest() sigs = [s.strip()[3:] for s in headers.get('X-Exo-Signature', '').split(',') if s.strip().startswith('v1=')] return any(hmac.compare_digest(s, expected) for s in sigs)L’en-tête X-NH-Signature, qui signe uniquement le corps sans horodatage, est déprécié et sera supprimé le 30 juin 2027.
Répondre et gérer les doublons
Section intitulée « Répondre et gérer les doublons »- Répondez avec n’importe quel code
2xxdans un délai de 5 secondes. Si vous devez effectuer un traitement long, mettez-le en file d’attente et répondez immédiatement. - La livraison est garantie au moins une fois : le même événement peut arriver plusieurs fois. Utilisez
idouX-Exo-Deliverypour reconnaître les doublons, par exemple en conservant les identifiants vus au cours des 5 dernières minutes. - Les événements peuvent arriver dans un ordre différent de celui dans lequel ils se sont produits : triez-les avec
created_at.