Sites API
The /api/v1 sites surface — list, get, launch, resume, delete, env, cron/workers, stop/start, tag, and the live launch stream.
Everything the panel does is available on the API. The web wizard and any REST or mobile client all call the same procedures. This page documents the sites surface. Each procedure requires a permission, and the server enforces it — the access rules below are real, not just documentation.
Transport
Procedures are reached over oRPC at /api/rpc/* (typed client) and exposed on /api/v1 (OpenAPI).
The OpenAPI document is served at GET /api/v1/openapi.json — it's generated from the same router
that handles the requests, so it always matches the live API. Un-routed procedures fall back to
POST /api/v1/<dotted.path>. The agent-facing protocol types live in @spipcp/protocol.
Permissions
| Permission | Grants |
|---|---|
sites:view | List, get, watch a launch, read env/cron. Held by operators. |
sites:launch | Create + launch a site (admin-only). |
sites:manage | Resume, delete, tag, env upsert, cron add/remove, stop/start (admin-only). |
sites:credential-reveal | Reveal a site env secret (audited, admin-only). |
Operators hold only sites:view (browse sites + status). Launching, managing, and credential-reveal
on sites are admin-only — those procedures return 403 for an operator.
Read (sites:view)
sites.list
sites:view — { limit?: 1..200, offset? } → SiteSummary[]. Every site, newest-first (the global
Sites page).
sites.get
sites:view — { siteId } → SiteSummary & { runs: LaunchRun[] }. One site with its launch-run
history (the workspace overview). 404 if unknown.
SiteSummary fields: id, instanceId, name, type, blueprintId, blueprintConfigVersion, typeConfig, servedPort, status, failureRemedy, databaseId, tag, createdAt, updatedAt. status is one of
launching | live | failed | stopped; failureRemedy is set only when status is failed.
sites.watchLaunch
sites:view — { siteId, runId } → AsyncIterable<{ stepId, status, detail?, changed?, done? }>.
A live stream of a launch run: each step (including any steps to install a missing stack along the
way) is sent as it completes, ending with a done frame. This is what the wizard's terminal shows.
Launch (sites:launch)
sites.launch
sites:launch —
{ instanceId, name, type: "wordpress" | "php" | "static" | "node",
typeConfig?, servedPort?: 1..65535, tag? }
→ { siteId, runId }Creates the site and starts its launch atomically — if anything fails, the whole thing rolls
back. SpipCP installs any missing stack along the way, provisions the database if the blueprint needs
one, runs the launch steps, and finishes with a smoke test. A failed smoke test leaves the site
failed with a remedy — never live. Audited as site.launch.
Manage (sites:manage)
sites.resume
sites:manage — { siteId, runId } → { resuming: true, runId }. Re-runs a failed launch,
picking up from the step that failed rather than starting over. Returns 409 if the run isn't
failed (only a failed run can resume), 404 if the site/run is unknown. Audited as
site.launch.resume.
sites.delete
sites:manage — { siteId } → { deleted: true }. Deletes the site and all its related data
(sources, env, cron/workers, launch runs). 404 if unknown. Audited.
sites.setTag
sites:manage — { siteId, tag: string(≤128) } → { tag: string | null }. Sets or clears the
free-text organisation tag. Audited.
sites.stop
sites:manage — { siteId } → { status: "stopped", appliesOnNextReconcile: true }. Marks the site
stopped; it's actually stopped on the next reconcile. Audited.
sites.start
sites:manage — { siteId } → { status: "launching", runId }. Relaunches the site (steps already
done are skipped). Returns the run id so you can stream it live. Audited.
Env (sites:view read / sites:manage write)
sites.env.list
sites:view — { siteId } → { vars: { key, secret, hasValue }[], contract: { key, label, required, secret }[] }. What the env editor reads: the site's vars (a secret's value is never returned in
plaintext) plus the blueprint's env contract, so the editor knows which vars are required, optional,
or secret.
sites.env.upsert
sites:manage — { siteId, key: SCREAMING_SNAKE_CASE, value } → { saved: true, redeployRequired: true }. The key is checked against the blueprint's env contract — a key the blueprint doesn't declare
is rejected with 400. A secret var is stored encrypted. The new value takes effect inside the
instance on the next relaunch. Audited as site.env.upsert.
Cron & workers (sites:view read / sites:manage write)
sites.cron.list
sites:view — { siteId } → CronWorker[]. The site's cron timers + workers (the cron & workers tab
read).
sites.cron.add
sites:manage —
{ siteId, kind: "cron" | "worker", name: string(≤64),
schedule?: systemd-OnCalendar, command: string[] }
→ CronWorker & { applied: true }A cron needs a schedule (a systemd OnCalendar expression; missing ⇒ 400); a worker runs
continuously (restarts on failure, survives reboot). SpipCP records it, then applies the systemd
timer/unit inside the instance right away. Audited as site.cron.add.
sites.cron.remove
sites:manage — { siteId, id } → { removed: true }. Removes the timer/worker and deletes its unit
files inside the instance. 404 if unknown. Audited as site.cron.remove.
Mobile-app ready
These are the same procedures the web wizard and workspace call — a mobile client lists, launches, resumes, and edits env/cron through this same surface. There's no separate "mobile API".
Team access & multi-tenancy
A planned, not-yet-built capability — assigning users to specific sites, nodes, or instances with a scoped role instead of one global role per user. This page records the decision, not shipped behaviour.
Instances API
The /api/v1 instances surface — list, CRUD, lifecycle, limits, readiness, console, snapshots.