Перейти до вмісту

Вихідні вебхуки

З вихідним вебхуком exoCortex надсилає запит POST до твоєї системи щоразу, коли стається подія, на яку ти підписався: створено тікет, змінено статус, спливає SLA. Так твоя система реагує одразу, без періодичного опитування API.

  1. В exoCortex Core відкрий Integrations → Webhooks. Потрібна адміністративна роль.
  2. Вкажи URL, який отримуватиме події. Він має починатися з https://.
  3. Вибери події. * означає всі події.
  4. Вибери формат:
    • json: стандартний формат exoCortex, описаний на цій сторінці;
    • datadog, pagerduty, slack-block: рідний формат цих сервісів, який слід використовувати, коли URL належить їм.
  5. Якщо цього вимагає твій шлюз, додай фіксовані заголовки, наприклад заголовок автентифікації.
  6. Збережи. Якщо ти не вказав секрет підпису, exoCortex згенерує його (whsec_…) і покаже лише один раз: збережи його, він потрібен для перевірки підписів.

Тестовою дією ти надсилаєш у підписку подію webhook.test і перевіряєш, що твоя система її отримує.

Подія Коли надходить
ticket.created Створено тікет
ticket.comment_added Додано коментар
ticket.field_changed Змінилося поле
ticket.assignee_changed Змінився виконавець
ticket.status_changed Змінився статус
ticket.priority_changed Змінився пріоритет
ticket.sla_warning SLA ось-ось спливе
ticket.sla_breach SLA сплив
ticket.resolved Тікет вирішено
ticket.closed Тікет закрито
ticket.reopened Тікет повторно відкрито
ticket.link_created Створено зв’язок між тікетами
ticket.link_deleted Видалено зв’язок між тікетами
routing.no_match Жодне правило маршрутизації не знайшло черги для тікета
routing.manual_routed Оператор спрямував тікет вручну
webhook.test Ти надіслав у підписку тестову подію

Крім цих, можна підписатися за назвою на події правил сповіщень свого тенанта, наприклад approval_requested.

exoCortex може додавати нові типи подій: твоя система має ігнорувати ті, яких не знає.

Кожна подія у форматі json має однакову структуру:

{
"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 ідентифікує подію і однаковий для всіх підписок, які її отримують.
  • actor — користувач, який спричинив подію, або system.
  • changes містить деталі події: наприклад, попередній і новий статус або текст коментаря.

До 30 червня 2027 року тіло містить також застарілі поля event, timestamp і source та копію полів із data на першому рівні. Нова система має читати лише описані вище поля.

Заголовок Приклад Значення
X-Exo-Timestamp 1793491200 Момент підпису в секундах Unix
X-Exo-Signature v1=5257a869… Підпис події
X-Exo-Event ticket.created Тип події, такий самий, як type
X-Exo-Delivery dlv_0b9d… Унікальний ідентифікатор спроби доставки

Перш ніж довіряти події, перевір підпис:

  1. Читай тіло саме в тому вигляді, у якому воно надійшло, не розбираючи й не переписуючи JSON: інакше зміняться байти.
  2. Відхиляй запит, якщо X-Exo-Timestamp немає або він відрізняється від твого годинника більш ніж на 300 секунд. Так ти блокуєш повторні запити. Синхронізуй годинник через NTP.
  3. Обчисли HMAC-SHA256 від timestamp + "." + body із секретом підписки в шістнадцятковому вигляді малими літерами.
  4. Порівняй результат за сталий час з кожним значенням v1= у X-Exo-Signature. Під час ротації секрету заголовок може містити кілька значень, розділених комами.

Приклад на 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);
});
}

Приклад на 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)

Заголовок X-NH-Signature, який підписує лише тіло без позначки часу, застарів і буде вилучений 30 червня 2027 року.

Як відповідати й обробляти дублікати

Section titled “Як відповідати й обробляти дублікати”
  • Відповідай будь-яким кодом 2xx протягом 5 секунд. Якщо потрібна тривала робота, постав її в чергу і відповідай одразу.
  • Доставку гарантовано щонайменше один раз: та сама подія може надійти більше ніж один раз. Використовуй id або X-Exo-Delivery, щоб розпізнавати дублікати, наприклад зберігаючи ідентифікатори, отримані за останні 5 хвилин.
  • Події можуть надходити не в тому порядку, у якому вони сталися: впорядковуй їх за created_at.