Skip to content

Webhook events

Webhooks in the JSON format are a POST with this body. Setting them up: Webhooks.

{
"id": "…",
"event": "server.state_changed",
"createdAt": "2026-10-11T12:00:00Z",
"tenantId": "…",
"server": { "id": "…", "name": "DayZ" },
"data": { "state": "offline" }
}
Event data
server.state_changed { "state" }: only running, offline or installFailed, sent once the state has held for 15 seconds and differs from the last one sent. For every change, use the console stream.
server.created { "template", "node" }
server.deleted {}
server.install_finished { "succeeded", "exitCode" }
ping sent by Test
  • X-Panel-Event: the event
  • X-Panel-Delivery: a unique id; use it to de-duplicate retries
  • X-Panel-Signature: t=1760184000,v1=<hex HMAC-SHA256(secret, "1760184000." + raw body)>
  1. Recompute the HMAC over t + "." + body, using the raw bytes as received.
  2. Compare in constant time.
  3. Reject timestamps older than a few minutes.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(secret, header, rawBody) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest();
const given = Buffer.from(v1, 'hex');
return given.length === expected.length && timingSafeEqual(given, expected);
}
  • Any 2xx answer counts as delivered.
  • Otherwise the panel retries after 1, 2, 4… up to 60 minutes, 8 attempts in total.
  • Redirects are not followed.