Zum Inhalt springen

Ausgehende Webhooks

Mit einem ausgehenden Webhook sendet exoCortex jedes Mal eine POST-Anfrage an Ihr System, wenn ein Ereignis eintritt, das Sie abonniert haben: ein erstelltes Ticket, eine Statusänderung, ein bald ablaufendes SLA. So reagiert Ihr System sofort, ohne die API in Intervallen abzufragen.

  1. Öffnen Sie in exoCortex Core Integrations → Webhooks. Sie benötigen eine administrative Rolle.
  2. Geben Sie die URL an, die die Ereignisse empfangen soll. Sie muss mit https:// beginnen.
  3. Wählen Sie die Ereignisse. * bedeutet alle Ereignisse.
  4. Wählen Sie das Format:
    • json: das Standardformat von exoCortex, das auf dieser Seite beschrieben ist;
    • datadog, pagerduty, slack-block: das native Format dieser Services, zu verwenden, wenn die URL zu ihnen gehört.
  5. Wenn Ihr Gateway es verlangt, fügen Sie feste Header hinzu, zum Beispiel einen Authentifizierungs-Header.
  6. Speichern Sie. Wenn Sie kein Signatur-Secret angegeben haben, erzeugt exoCortex eines (whsec_…) und zeigt es nur einmal an: Speichern Sie es, Sie brauchen es zur Prüfung der Signaturen.

Mit der Testaktion senden Sie das Ereignis webhook.test an das Abonnement und prüfen, ob Ihr System es empfängt.

Ereignis Wann es eintrifft
ticket.created Ein Ticket wurde erstellt
ticket.comment_added Ein Kommentar wurde hinzugefügt
ticket.field_changed Ein Feld hat sich geändert
ticket.assignee_changed Der Bearbeiter hat sich geändert
ticket.status_changed Der Status hat sich geändert
ticket.priority_changed Die Priorität hat sich geändert
ticket.sla_warning Ein SLA läuft bald ab
ticket.sla_breach Ein SLA ist abgelaufen
ticket.resolved Das Ticket wurde gelöst
ticket.closed Das Ticket wurde geschlossen
ticket.reopened Das Ticket wurde wieder geöffnet
ticket.link_created Eine Verknüpfung zwischen Tickets wurde erstellt
ticket.link_deleted Eine Verknüpfung zwischen Tickets wurde entfernt
routing.no_match Keine Routing-Regel hat eine Warteschlange für das Ticket gefunden
routing.manual_routed Ein Bearbeiter hat das Ticket manuell weitergeleitet
webhook.test Sie haben ein Testereignis an das Abonnement gesendet

Darüber hinaus können Sie die Ereignisse der Benachrichtigungsregeln Ihres Tenants namentlich abonnieren, zum Beispiel approval_requested.

exoCortex kann neue Ereignistypen hinzufügen: Ihr System muss unbekannte Typen ignorieren.

Jedes Ereignis im Format json hat dieselbe Struktur:

{
"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 identifiziert das Ereignis und ist für alle Abonnements, die es empfangen, gleich.
  • actor ist der Benutzer, der das Ereignis ausgelöst hat, oder system.
  • changes enthält die Details des Ereignisses: zum Beispiel vorherigen und neuen Status oder den Text des Kommentars.

Bis zum 30. Juni 2027 enthält der Body auch die veralteten Felder event, timestamp und source sowie eine Kopie der Felder aus data auf oberster Ebene. Ein neues System sollte nur die oben beschriebenen Felder lesen.

Header Beispiel Bedeutung
X-Exo-Timestamp 1793491200 Zeitpunkt der Signatur, in Unix-Sekunden
X-Exo-Signature v1=5257a869… Signatur des Ereignisses
X-Exo-Event ticket.created Ereignistyp, identisch mit type
X-Exo-Delivery dlv_0b9d… Eindeutige Kennung des Zustellversuchs

Bevor Sie einem Ereignis vertrauen, prüfen Sie die Signatur:

  1. Lesen Sie den Body so, wie er angekommen ist, ohne das JSON zu parsen und neu zu schreiben: Sonst ändern sich die Bytes.
  2. Verwerfen Sie die Anfrage, wenn X-Exo-Timestamp fehlt oder um mehr als 300 Sekunden von Ihrer Uhr abweicht. So blockieren Sie wiederholte Anfragen. Halten Sie Ihre Uhr per NTP synchron.
  3. Berechnen Sie den HMAC-SHA256 von timestamp + "." + body mit dem Secret des Abonnements, in hexadezimalen Kleinbuchstaben.
  4. Vergleichen Sie das Ergebnis in konstanter Zeit mit jedem v1=-Wert aus X-Exo-Signature. Während der Rotation des Secrets kann der Header mehrere Werte enthalten, durch Kommas getrennt.

Beispiel 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);
});
}

Beispiel 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)

Der Header X-NH-Signature, der nur den Body ohne Zeitstempel signiert, ist veraltet und wird am 30. Juni 2027 entfernt.

  • Antworten Sie innerhalb von 5 Sekunden mit einem beliebigen 2xx-Code. Wenn Sie eine längere Verarbeitung durchführen müssen, stellen Sie sie in eine Warteschlange und antworten Sie sofort.
  • Die Zustellung erfolgt mindestens einmal: Dasselbe Ereignis kann mehrfach eintreffen. Verwenden Sie id oder X-Exo-Delivery, um Duplikate zu erkennen, zum Beispiel indem Sie die in den letzten 5 Minuten gesehenen Kennungen speichern.
  • Ereignisse können in einer anderen Reihenfolge eintreffen, als sie aufgetreten sind: Sortieren Sie sie nach created_at.