API

API: дані webhook

JSON, який Tenium надсилає на контакти webhook, коли монітор не працює, відновлюється чи попереджає про термін дії, а також заголовки й перевірка підпису.

Контакти webhook отримують POST із Content-Type: application/json на кожну подію. Тіло запиту — те саме повідомлення, яке Tenium формує для e-mail і чатів.

Події

Поле event (його також передають у заголовку X-Tenium-Event) має одне з таких значень:

  • down: монітор підтверджено недоступним
  • blocked: чекер зупиняє WAF або захист від ботів; сам сайт, імовірно, працює
  • up: монітор відновився (надсилається лише контактам, які отримали сповіщення про збій)
  • degraded: час відповіді перевищує поріг або змінився вміст сторінки
  • heartbeat_missed: heartbeat-монітор не надіслав пінг вчасно
  • ssl_expiry: термін дії сертифіката SSL спливає або вже сплив
  • domain_expiry: термін дії домену спливає або вже сплив
  • test: тестове сповіщення зі сторінки контакту

Дані події

{
  "version": 1,
  "event": "down",
  "kind": "down",
  "title": "DOWN",
  "summary": "Client A shop · 503 Service Unavailable · confirmed from Frankfurt (FRA), London (LON), New York (NYC)",
  "color": "red",
  "sentAt": "2026-01-19T09:39:14.000Z",
  "region": { "code": "fra", "name": "Frankfurt" },
  "account": { "id": "0f8e...", "name": "Your Studio", "brandName": null, "brandEmailFrom": null, "brandEmailVerifiedAt": null, "timezone": "Europe/Tallinn" },
  "client": { "id": "9a8b...", "name": "Client A" },
  "monitor": {
    "id": "a3f1...",
    "name": "Client A shop",
    "type": "http",
    "target": "https://shop.clienta.com",
    "url": "https://app.tenium.app/app/monitors/a3f1..."
  },
  "incident": {
    "id": "e5b4...",
    "severity": "down",
    "cause": "503 Service Unavailable",
    "statusCode": 503,
    "startedAt": "2026-01-19T09:39:12.000Z",
    "resolvedAt": null,
    "durationSec": 2,
    "confirmedRegions": ["fra", "lon", "nyc"],
    "acknowledgedAt": null,
    "upstreamProvider": null,
    "lastResponse": null,
    "url": "https://app.tenium.app/app/incidents/e5b4...",
    "ackUrl": "https://app.tenium.app/app/incidents/e5b4...?action=ack"
  },
  "expiry": null,
  "nextStep": "Check the server / hosting panel. Acknowledge so the team knows you are on it."
}

kind — внутрішній тип сповіщення; він збігається з event, окрім blocked, яке приходить як kind: "down". Для up заповнені resolvedAt і durationSec. Для подій про термін дії incident дорівнює null, а замість нього передається expiry: { "what": "SSL certificate", "expiresAt": "...", "daysLeft": 7, "issuer": "..." }. Для моніторів поза простором клієнта client дорівнює null.

Заголовки

Кожен запит містить:

  • X-Tenium-Event: назва події, наприклад down
  • X-Tenium-Delivery: унікальний ідентифікатор цієї доставки
  • X-Tenium-Timestamp: час sentAt з даних події
  • X-Tenium-Signature: є, якщо для контакту задано секрет підпису

Підпис

X-Tenium-Signature: t=1737279554,v1=5f3a...

t — час Unix у секундах, коли запит підписано. v1 — HMAC-SHA256 від "{t}.{rawBody}" із секретом підпису контакту (його показують під час створення контакту). Відхиляйте запити старші за 5 хвилин.

Приклад на Node:

import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Повторні спроби

Відповідайте будь-яким кодом 2xx протягом 10 секунд. Помилки мережі, таймаути та відповіді 5xx і 429 повторюються: до 3 спроб загалом із паузою 30 секунд. Інші відповіді 4xx не повторюються.