Salta ai contenuti

Webhook in uscita

Con un webhook in uscita exoCortex invia una richiesta POST al tuo sistema ogni volta che succede un evento a cui ti sei iscritto: un ticket creato, un cambio di stato, uno SLA in scadenza. Così il tuo sistema reagisce subito, senza interrogare l’API a intervalli.

  1. In exoCortex Core apri Integrations → Webhooks. Serve un ruolo amministrativo.
  2. Indica l’URL che riceverà gli eventi. Deve essere https://.
  3. Scegli gli eventi. * significa tutti gli eventi.
  4. Scegli il formato:
    • json: il formato standard di exoCortex, descritto in questa pagina;
    • datadog, pagerduty, slack-block: il formato nativo di quei servizi, da usare quando l’URL è il loro.
  5. Se il tuo gateway lo richiede, aggiungi intestazioni fisse, per esempio un’intestazione di autenticazione.
  6. Salva. Se non hai indicato un segreto di firma, exoCortex ne genera uno (whsec_…) e lo mostra una sola volta: salvalo, ti serve per verificare le firme.

Con l’azione di test invii alla sottoscrizione l’evento webhook.test e controlli che il tuo sistema lo riceva.

Evento Quando arriva
ticket.created È stato creato un ticket
ticket.comment_added È stato aggiunto un commento
ticket.field_changed È cambiato un campo
ticket.assignee_changed È cambiato l’assegnatario
ticket.status_changed È cambiato lo stato
ticket.priority_changed È cambiata la priorità
ticket.sla_warning Uno SLA sta per scadere
ticket.sla_breach Uno SLA è scaduto
ticket.resolved Il ticket è stato risolto
ticket.closed Il ticket è stato chiuso
ticket.reopened Il ticket è stato riaperto
ticket.link_created È stato creato un collegamento tra ticket
ticket.link_deleted È stato rimosso un collegamento tra ticket
routing.no_match Nessuna regola di routing ha trovato una coda per il ticket
routing.manual_routed Un operatore ha instradato il ticket a mano
webhook.test Hai inviato un evento di prova alla sottoscrizione

Oltre a questi puoi iscriverti per nome agli eventi delle regole di notifica del tuo tenant, per esempio approval_requested.

exoCortex può aggiungere nuovi tipi di evento: il tuo sistema deve ignorare quelli che non conosce.

Ogni evento in formato json ha la stessa struttura:

{
"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" }
}
}
  • id identifica l’evento ed è lo stesso per tutte le sottoscrizioni che lo ricevono.
  • actor è l’utente che ha causato l’evento, oppure system.
  • changes contiene i dettagli dell’evento: per esempio stato precedente e nuovo, o il testo del commento.

Fino al 30 giugno 2027 il corpo contiene anche i campi deprecati event, timestamp e source e una copia dei campi di data al primo livello. Un nuovo sistema deve leggere solo i campi descritti sopra.

Intestazione Esempio Significato
X-Exo-Timestamp 1793491200 Momento della firma, in secondi Unix
X-Exo-Signature v1=5257a869… Firma dell’evento
X-Exo-Event ticket.created Tipo di evento, uguale a type
X-Exo-Delivery dlv_0b9d… Identificativo univoco del tentativo di consegna

Prima di fidarti di un evento, verifica la firma:

  1. Leggi il corpo così come è arrivato, senza interpretare e riscrivere il JSON: cambierebbero i byte.
  2. Scarta la richiesta se X-Exo-Timestamp manca o si discosta di più di 300 secondi dal tuo orologio. Così blocchi le richieste ripetute. Tieni l’orologio sincronizzato con NTP.
  3. Calcola l’HMAC-SHA256 di timestamp + "." + body con il segreto della sottoscrizione, in esadecimale minuscolo.
  4. Confronta il risultato, in tempo costante, con ogni valore v1= di X-Exo-Signature. Durante la rotazione del segreto l’intestazione può contenerne più di uno, separati da virgole.

Esempio in 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);
});
}

Esempio in 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’intestazione X-NH-Signature, che firma solo il corpo senza timestamp, è deprecata e sarà rimossa il 30 giugno 2027.

  • Rispondi con un qualsiasi codice 2xx entro 5 secondi. Se devi fare un lavoro lungo, mettilo in coda e rispondi subito.
  • La consegna è garantita almeno una volta: lo stesso evento può arrivare più di una volta. Usa id o X-Exo-Delivery per riconoscere i duplicati, per esempio conservando gli identificativi visti negli ultimi 5 minuti.
  • Gli eventi possono arrivare in ordine diverso da quello in cui sono avvenuti: ordinali con created_at.