SpipCP
Reference

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

PermissionGrants
probes:viewList, get, history, watch a probe's live state. Held by operators.
probes:manageCreate, 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? }
  → ProbeSummary

Create 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/:token

The 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...c1

Mobile-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.

On this page