Monitoring API
The /api/v1 probes surface — list, get, history, watch (live), create, update, pause, archive — plus the public token-authed heartbeat ping endpoint.
Everything the panel does is available on the API. This page documents the probes surface — the uptime monitoring API — and the public heartbeat ping endpoint. Each procedure requires a permission, enforced by the server.
Transport
Procedures are reached over oRPC at /api/rpc/* (typed client) and exposed on /api/v1 (OpenAPI).
The heartbeat ping is the one exception — it lives outside the RBAC API at
/api/heartbeat/:token because it is a public, token-scoped write (see the bottom of this page).
Permissions
| Permission | Grants |
|---|---|
probes:view | List, get, history, watch a probe's live state. Held by operators. |
probes:manage | Create, update, pause/resume, archive probes + notification rules (admin-only). |
Operators hold only probes:view (browse the Monitoring page + per-entity Health tabs). Creating and
editing probes and notification rules is admin-only — those procedures return 403 for an
operator.
Read (probes:view)
probes.list
probes:view — { targetKind?, targetId?, includeArchived?: boolean } → ProbeSummary[]. The Monitoring
grid (newest-first); filter to one entity's probes (e.g. { targetKind: "site", targetId }) for a
Health tab.
ProbeSummary fields: id, name, type, targetKind, targetId, target, state, enabled, consecutiveFailures, lastResult, lastCheckedAt, lastChangedAt, config, notifyConfig, hasWebhookSecret, archivedAt, createdAt. state is up | down | degraded | flapping | paused. The webhook secret is
never returned — only hasWebhookSecret.
probes.get
probes:view — { id } → ProbeSummary & { events: ProbeResultRow[] }. One probe with its recent
state-change events (the incident log — there is no separate incidents table). 404 if unknown.
probes.history
probes:view — { id, granularity?: "raw" | "hourly" | "daily", hours?: 1..2160 } → ProbeResultRow[].
History for the per-entity chart. Charts read the hourly rollups, never the raw table; upRatio
is basis points (0–10000).
probes.watch
probes:view —
{ targetKind?, targetId?, intervalMs?: 250..10000 }
→ AsyncIterable<{ id, name, state, enabled, lastChangedAt }>A live state stream. It sends a frame whenever a probe's (state, enabled, lastChangedAt)
changes — a service going down flips its probe up → down and a frame is sent. This is what the
Monitoring page subscribes to so the grid updates live. Optionally scoped to one entity for a
Health tab. The stream closes itself after a while; the client reopens it.
Manage (probes:manage)
probes.create
probes:manage —
{ name, type: "http" | "tcp" | "dns" | "cert" | "heartbeat",
targetKind: "node" | "instance" | "site" | "domain" | "external",
targetId?, target, config?, notifyConfig?, webhookSecret?, enabled? }
→ ProbeSummaryCreate a probe (manual + external probes). The webhookSecret is stored encrypted. A new probe
starts paused until the first scan finds its real state, so it never shows green before it's been
checked. Audited as probe.create.
probes.update
probes:manage — { id, name?, config?, notifyConfig?, enabled?, webhookSecret? } → ProbeSummary.
Patch a probe's config / notify rules / enabled state / webhook secret. For the secret, undefined
leaves it, a string re-encrypts, null clears it. 404 if unknown. Audited as probe.update.
probes.setEnabled
probes:manage — { id, enabled: boolean } → { enabled }. Pause/resume (a thin enable toggle — no
scans while paused). 404 if unknown. Audited as probe.pause / probe.resume.
probes.archive
probes:manage — { id } → { archived: true }. Archive the probe — it stops being scanned, but its
result history and event log survive. 404 if unknown. Audited as probe.archive.
config & notifyConfig
config holds the per-type settings: the check interval, the consecutive-failure threshold
and flap window (which smooth out brief blips), the cert-warn-days for a cert probe, and a
heartbeat's interval + grace. notifyConfig holds the delivery settings: the email recipients,
the cooldown, max-per-hour, and repeat-while-down cadence. Both are validated on write, so
a bad value never takes effect. See the Monitoring concept for what each means.
The heartbeat ping endpoint (public, token-authed)
GET /api/heartbeat/:token
POST /api/heartbeat/:tokenThe one probe surface that needs no permission. A cron job or dead-man's-switch pings it on every
run; the token in the URL is the credential (a random per-probe secret). The matching heartbeat
probe checks how recently it was pinged (interval + grace) to decide up/down. Both GET and
POST record a ping, so a bare curl from cron works. An unknown, disabled, or archived token
returns 404 — no hint that a token exists.
# A backup script pings its heartbeat on every successful run:
curl -fsS https://panel.example.com/api/heartbeat/3f9a...c1Mobile-app ready
The probes procedures are the same ones the Monitoring page + Health tabs call. A mobile client lists, watches live, and manages probes through this identical surface.
Domains & Routes API
The /api/v1 domains + routes surface — list, get, attach, renew, detach, the orange-cloud toggle, driver list, test-credentials, provider DNS accounts, the self-hosted nameserver/zone endpoints, and the http/raw route CRUD.
On-demand ask contract
The frozen contract an app implements to authorize on-demand customer-domain certificates — a single GET the node calls before Caddy issues, 200 to allow, anything else to deny. For app authors.