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: название события, напримерdownX-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 не повторяются.