SpipCP
Reference

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

PermissionGrants
sites:viewList, get, watch a launch, read env/cron. Held by operators.
sites:launchCreate + launch a site (admin-only).
sites:manageResume, delete, tag, env upsert, cron add/remove, stop/start (admin-only).
sites:credential-revealReveal 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".

On this page