Status Pages
Your public status page goes live in minutes. Define components + HTTP probe checks; the worker probes every 30 seconds and opens an auto-incident on sustained failure (optionally firing a restart target). Your end users subscribe by email, Telegram, or webhook — incident updates ship to them automatically.
Setup#
This lives on status.sentroy.com (not auth.sentroy.com). Get your first page live in 1 minute.
- Visit status.sentroy.com/d → log in with your Sentroy account → pick your company.
- Create the status page — the wizard asks for a name and a public slug (e.g.
my-app). Public URL becomeshttps://status.sentroy.com/p/my-app. - Add components — the user-facing service groups (API, Database, Web App). Each component is monitored by one or more HTTP checks.
- Define HTTP checks — per component: probe URL + interval (30s-3600s) + expected status range + optional degraded-latency threshold + optional auto-restart target binding.
- Settings → Branding — display name, primary color, logo URL, optional logo link, and tagline. Live preview reflects every change immediately.
Components & checks#
Each user-visible service on the public page is a component. Component status is derived from the worst severity across its checks.
Status derivation#
For each component, given its bound checks:
- any check
down→ component is down (red) - any check
degraded(responds 2xx but slow) → degraded (amber) - every check
no-data→ component is no-data (gray — never probed yet) - otherwise → operational (green)
- if an active maintenance window covers this component, the status is overridden to maintenance (blue). Downtime during maintenance does NOT count against uptime.
HTTP check fields#
url+method(GET/POST/HEAD)intervalSeconds— 30 minimum, 3600 maximumexpectedStatusMin/Max— defaults 200-299; define your own success rangeexpectedBodyContains— optional; if the response body lacks this string, marked downtimeoutMs+degradedLatencyMs— timeout exceeds triggers down; degradedLatency triggers degradedinsecureSkipTlsVerify— dev only
Incidents#
Communicate outages to your users — open one manually or let the worker do it for you.
Two types:
- Manual — dashboard Incidents tab → "New incident". Provide title, impact (minor/major/critical), affected components, and an initial update message (TR + EN). Status: investigating → identified → monitoring → resolved.
- Auto — the worker opens an incident when a check sees 3+ consecutive failures. It auto-resolves after 30 minutes of recovered operation. Auto incidents carry a
source: "auto"badge; you can still post manual updates or resolve them yourself.
Timeline updates#
Every status transition + commentary is pushed as a new update. Update bodies are localized ({ tr, en }) — the public page renders the user's browser locale.
Maintenance windows#
Announce planned downtime in advance. Subscribers get notified, a banner appears on the public page, and uptime stats are unaffected.
Dashboard Maintenance tab → "Schedule maintenance". Set start/end, affected components, and a title + description (TR + EN). State machine:
scheduled— future start time. A reminder email is sent 1 hour before.in_progress— UI "Start now" button or automatic when scheduledStart passes. Subscribers receive the "started" notification.completed— UI "Mark complete" button. Subscribers receive the "completed" notification.cancelled— cancelled before starting.
Restart targets#
Endpoints the worker calls automatically when a check fails N times in a row. Three target types: HTTP, SSH, and Coolify built-in.
HTTP target#
Your own /restart route, a Coolify webhook, or any HTTP endpoint. POST or GET, optional encrypted auth header, optional body, expected status range, timeout.
SSH target#
The worker SSHes into a remote host with your private key (PEM, stored AES-256-GCM encrypted) and runs a single command (e.g. docker restart api-server). Optional passphrase. Exit code 0 = success.
Coolify built-in target#
The worker calls Coolify's GET /api/v1/deploy?uuid=...&force=true endpoint with your API token (encrypted). Coolify queues the redeploy.
Bind a target to a check from Checks tab → check edit → "Auto-restart on failure" section → pick the target + threshold (N consecutive failures) + cooldown (minimum seconds between restarts; default 600 = 10 min).
The "test fire" button (⚡) in the dashboard manually triggers an HTTP target without going through threshold/cooldown — useful for verifying setup. SSH and Coolify targets only fire from scheduled execution (no manual test in v1).
Subscribers#
Your users subscribe on the public page by email, Telegram, or webhook. Each incident update + maintenance event triggers a notification automatically.
Three channels#
- Email — double opt-in. The user enters an address, gets a confirmation link, clicks it, and is verified. Every notification carries a one-click "Unsubscribe" link.
- Telegram — enter a chat ID and a bot token from
@BotFather. The chat owner must send/startto the bot first. Sentroy sends a confirmation message to verify; the bot token is stored AES-256-GCM encrypted. No verification email step. - Webhook — server-to-server. Sentroy POSTs HMAC-SHA256 signed payloads to your URL. No verification step (URL owner has implicit consent).
Topic + component filters#
From the subscribe dialog, users can pick:
- Topic — everything / incidents only / maintenance only
- Components — only notify for events that affect these specific components (empty = all components)
Users can change these later from /p/[slug]/preferences?token=... (token is in every notification email footer).
Webhook signup example#
curl -X POST https://status.sentroy.com/api/v1/status/my-app/subscribe \
-H "Content-Type: application/json" \
-d '{
"type": "webhook",
"webhookUrl": "https://your-app.example.com/sentroy-status-webhook"
}'
# Response: { "managementToken": "...", "webhookSecret": "swhs_..." }
# managementToken is the HMAC secret; shown once, store it now.Sample webhook payload (incident update):
{
"event": "incident.updated",
"pageSlug": "my-app",
"data": {
"incidentId": "...",
"incidentTitle": { "tr": "...", "en": "..." },
"impact": "major",
"affectedComponentIds": ["..."],
"update": {
"id": "...",
"status": "monitoring",
"body": { "tr": "...", "en": "..." },
"createdAt": "2026-05-17T12:34:56Z"
}
},
"timestamp": "2026-05-17T12:34:56.789Z"
}Event types#
Sent as the X-Sentroy-Event header on webhook deliveries:
incident.updated— new timeline update (investigating / identified / monitoring)incident.resolved— incident closedmaintenance.scheduled— new maintenance window createdmaintenance.reminder— 1 hour before startmaintenance.started— window beginsmaintenance.completed— window ends
Public snapshot API#
JSON snapshot of your status page — CORS-open, no auth required. Use it for embed widgets, monitoring dashboards, or your own UI.
GET /api/v1/status/[slug] — 30s ISR cache. Pass ?lang=tr or send an Accept-Language header to get incident and maintenance text in the requested locale (defaults to en).
curl -s https://status.sentroy.com/api/v1/status/my-app?lang=en | jqResponse shape:
{
"page": {
"name": "My App",
"slug": "my-app",
"branding": {
"displayName": "My App Status",
"primaryColor": "#3b82f6",
"logoUrl": null,
"logoLinkUrl": null,
"tagline": null
},
"customDomain": null,
"subscribersEnabled": true
},
"overall": "operational",
"components": [
{
"id": "...",
"name": "API",
"status": "operational",
"uptime24h": 99.95,
"uptime30d": 99.99,
"lastCheckedAt": "2026-05-18T12:34:56Z",
"dailyHistory": [
{ "date": "2026-02-17", "status": "operational" },
...90 entries
],
"checks": [
{ "id": "...", "name": "Health endpoint", "status": "operational", "lastLatencyMs": 142, "lastCheckedAt": "..." }
]
}
],
"activeIncidents": [
{
"id": "...",
"title": "Mail delivery delays",
"status": "monitoring",
"impact": "major",
"affectedComponentIds": ["..."],
"startedAt": "...",
"updates": [{ "id": "...", "status": "monitoring", "body": "Fix deployed; watching.", "createdAt": "..." }]
}
],
"upcomingMaintenances": [],
"pastIncidents": [
{ "id": "...", "title": "...", "impact": "minor", "startedAt": "...", "resolvedAt": "...", "affectedComponentIds": [] }
],
"generatedAt": "2026-05-18T12:34:58Z",
"windowHours": 24
}Embed widget#
Embed your status badge in your own site with an iframe. Locale-aware (?lang=), light/dark mode automatic.
The dashboard Overview → "Embed widget" panel shows the full snippet with your slug. Typical usage:
<iframe
src="https://status.sentroy.com/p/my-app/embed"
width="320"
height="80"
style="border:0"
loading="lazy"
></iframe>Webhook signature verification#
Sentroy signs every webhook payload with HMAC-SHA256. Your endpoint MUST verify the signature.
Sentroy signs each payload with the subscriber's managementToken using HMAC-SHA256. Check the X-Sentroy-Signature: sha256=<hex> header.
import { createHmac, timingSafeEqual } from "node:crypto"
function verifySentroyWebhook(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex")
const provided = (signatureHeader || "").replace(/^sha256=/, "")
if (provided.length !== expected.length) return false
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(provided, "hex"))
}
// Express example:
app.post("/sentroy-status-webhook", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifySentroyWebhook(
req.body, // RAW buffer — verify BEFORE JSON.parse
req.headers["x-sentroy-signature"],
process.env.SENTROY_WEBHOOK_SECRET, // managementToken from signup response
)
if (!ok) return res.status(401).end()
const event = JSON.parse(req.body.toString())
// ... your logic
res.json({ ok: true })
})