API
API: webhook payloads
The JSON Tenium sends to webhook contacts when a monitor goes down, recovers or warns about an expiry, with headers and signature verification.
Webhook contacts receive a POST with Content-Type: application/json for each event. The body is the same alert payload Tenium renders for e-mail and chat channels.
Events
The event field (also sent in the X-Tenium-Event header) is one of:
down: a monitor was confirmed downblocked: the checker is being challenged by a WAF or bot protection; the site itself is probably fineup: the monitor recovered (sent only to contacts that received the down alert)degraded: response time above the threshold, or page content changedheartbeat_missed: a heartbeat did not ping on timessl_expiry: the SSL certificate expires soon or has expireddomain_expiry: the domain expires soon or has expiredtest: the test alert sent from the contact page
Payload
{
"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 is the internal alert kind and equals event except for blocked, which arrives as kind: "down". For up, resolvedAt and durationSec are set. For expiry events incident is null and expiry is set instead: { "what": "SSL certificate", "expiresAt": "...", "daysLeft": 7, "issuer": "..." }. client is null for monitors outside a client workspace.
Headers
Every request includes:
X-Tenium-Event: the event name, for exampledownX-Tenium-Delivery: a unique ID for this deliveryX-Tenium-Timestamp: thesentAttime of the payloadX-Tenium-Signature: present when the contact has a signing secret
Signature
X-Tenium-Signature: t=1737279554,v1=5f3a...
t is the Unix time in seconds when the request was signed. v1 is HMAC-SHA256 of "{t}.{rawBody}" using the contact’s signing secret (shown when the contact is created). Reject requests older than 5 minutes.
Node example:
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));
}
Retries
Answer with any 2xx within 10 seconds. Network errors, timeouts, 5xx and 429 answers are retried: up to 3 attempts in total, 30 seconds apart. Other 4xx answers are not retried.