Przejdź do głównej zawartości

Webhooki wychodzące

Dzięki webhookowi wychodzącemu exoCortex wysyła żądanie POST do Państwa systemu za każdym razem, gdy wystąpi zasubskrybowane zdarzenie: utworzenie zgłoszenia, zmiana stanu, zbliżający się termin SLA. Dzięki temu system reaguje natychmiast, bez cyklicznego odpytywania API.

  1. W exoCortex Core należy otworzyć Integrations → Webhooks. Wymagana jest rola administracyjna.
  2. Należy podać URL, który będzie odbierać zdarzenia. Musi zaczynać się od https://.
  3. Należy wybrać zdarzenia. * oznacza wszystkie zdarzenia.
  4. Należy wybrać format:
    • json: standardowy format exoCortex, opisany na tej stronie;
    • datadog, pagerduty, slack-block: natywny format tych usług, do stosowania, gdy URL należy do nich.
  5. Jeśli wymaga tego Państwa brama, należy dodać stałe nagłówki, na przykład nagłówek uwierzytelniający.
  6. Zapisać. Jeśli nie podano sekretu podpisu, exoCortex generuje go (whsec_…) i wyświetla tylko raz: należy go zapisać, ponieważ jest potrzebny do weryfikacji podpisów.

Działanie testowe wysyła do subskrypcji zdarzenie webhook.test, co pozwala sprawdzić, czy Państwa system je odbiera.

Zdarzenie Kiedy jest wysyłane
ticket.created Utworzono zgłoszenie
ticket.comment_added Dodano komentarz
ticket.field_changed Zmieniło się pole
ticket.assignee_changed Zmieniła się osoba przypisana
ticket.status_changed Zmienił się stan
ticket.priority_changed Zmienił się priorytet
ticket.sla_warning Zbliża się termin SLA
ticket.sla_breach Termin SLA upłynął
ticket.resolved Zgłoszenie zostało rozwiązane
ticket.closed Zgłoszenie zostało zamknięte
ticket.reopened Zgłoszenie zostało ponownie otwarte
ticket.link_created Utworzono powiązanie między zgłoszeniami
ticket.link_deleted Usunięto powiązanie między zgłoszeniami
routing.no_match Żadna reguła routingu nie znalazła kolejki dla zgłoszenia
routing.manual_routed Operator ręcznie skierował zgłoszenie
webhook.test Wysłano zdarzenie testowe do subskrypcji

Oprócz nich można subskrybować według nazwy zdarzenia z reguł powiadomień Państwa tenanta, na przykład approval_requested.

exoCortex może dodawać nowe typy zdarzeń: Państwa system musi ignorować te, których nie zna.

Każde zdarzenie w formacie json ma tę samą 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 identyfikuje zdarzenie i jest taki sam dla wszystkich subskrypcji, które je otrzymują.
  • actor to użytkownik, który spowodował zdarzenie, lub system.
  • changes zawiera szczegóły zdarzenia: na przykład poprzedni i nowy stan albo treść komentarza.

Do 30 czerwca 2027 treść zawiera również wycofywane pola event, timestamp i source oraz kopię pól z data na pierwszym poziomie. Nowy system powinien odczytywać wyłącznie pola opisane powyżej.

Nagłówek Przykład Znaczenie
X-Exo-Timestamp 1793491200 Moment złożenia podpisu w sekundach Unix
X-Exo-Signature v1=5257a869… Podpis zdarzenia
X-Exo-Event ticket.created Typ zdarzenia, taki sam jak type
X-Exo-Delivery dlv_0b9d… Unikalny identyfikator próby doręczenia

Przed zaufaniem zdarzeniu należy zweryfikować podpis:

  1. Należy odczytać treść dokładnie w takiej postaci, w jakiej dotarła, bez parsowania i ponownego zapisywania JSON: zmieniłyby się bajty.
  2. Należy odrzucić żądanie, jeśli brakuje X-Exo-Timestamp lub różni się on o ponad 300 sekund od Państwa zegara. W ten sposób blokuje się powtórzone żądania. Zegar należy synchronizować przez NTP.
  3. Należy obliczyć HMAC-SHA256 z timestamp + "." + body za pomocą sekretu subskrypcji, w zapisie szesnastkowym małymi literami.
  4. Należy porównać wynik, w stałym czasie, z każdą wartością v1= z nagłówka X-Exo-Signature. Podczas rotacji sekretu nagłówek może zawierać więcej niż jedną wartość, rozdzielonych przecinkami.

Przykład w 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);
});
}

Przykład w Pythonie:

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)

Nagłówek X-NH-Signature, który podpisuje wyłącznie treść bez znacznika czasu, jest wycofywany i zostanie usunięty 30 czerwca 2027.

  • Należy odpowiedzieć dowolnym kodem 2xx w ciągu 5 sekund. Jeśli trzeba wykonać długotrwałą pracę, należy umieścić ją w kolejce i odpowiedzieć od razu.
  • Doręczenie jest gwarantowane co najmniej raz: to samo zdarzenie może dotrzeć więcej niż jeden raz. Do rozpoznawania duplikatów należy używać id lub X-Exo-Delivery, na przykład przechowując identyfikatory odebrane w ciągu ostatnich 5 minut.
  • Zdarzenia mogą docierać w innej kolejności niż ta, w której wystąpiły: należy je porządkować według created_at.