Skip to content
Super 1.7.0 is production-ready — transactional installs with automatic rollback, cron runtime caps (kill_after_secs), and panic-free input handling. See what’s new →

API Reference

Super exposes a RESTful API on port 9002 (default). All responses are in JSON format.

Authentication

OSS (no security plugin): Loopback binds leave the API open by default. A non-empty [server].auth_secret activates core auth (Bearer = that string). Non-loopback TCP without auth_secret (and without the security plugin) refuses to start. See Authentication.

Licensed ([license].key valid): security is bundled with every subscription and must load — otherwise superd refuses startup. API auth is always active when licensed. See Authentication — Licensed deployments require security.

With security plugin loaded: All API requests require Authorization: Bearer <token> (except /health and the docs whitelist: /api/docs, /api/v1/openapi.json, SPA / + /assets). /metrics requires Bearer when auth is on. Config auth_secret bootstraps Access Tokens and stays usable until an Admin explicitly disables it. Core auth stays off so there is one gate. See Authentication.

Health & docs

  • GET /health — liveness (no auth)
  • GET /api/docs — Swagger UI when enable_docs = true (docs feature build)
  • GET /api/v1/openapi.json — OpenAPI catalog (same docs gate / whitelist)

Programs

List Programs

Get a summary of all managed processes.

  • GET /api/v1/programs

Response:

[
  {
    "id": "a1b2c3d4-...",
    "name": "api-server",
    "status": "Running",
    "pid": 12345,
    "cpu_usage": 2.5,
    "mem_usage": 10485760
  }
]

Create Program

Register a new process dynamically.

  • POST /api/v1/programs

Body:

{
  "name": "worker-1",
  "command": "./worker",
  "autostart": true,
  "autorestart": "unexpected",
  "exitcodes": [0],
  "startsecs": 10,
  "retry_limit": 3
}
FieldDefaultDescription
autorestartunexpectedunexpected, true, or false (Supervisor-compatible)
exitcodes[0]Exit codes treated as success when autorestart=unexpected
startsecs10Seconds of stable run before exit resets retry counter

Note

autostart controls boot-time start only. To disable crash auto-restart, set "autorestart": "false".

Response: 201 Created with the new UUID(s). Validation failures return 400 with { "status": "error", "message": "..." }. The message names the field (command: …, health_check.url: …, depends_on: unknown service(s): … or depends_on: dependency cycle detected: …) and, when a name is set, program '…':. JSON syntax / unknown fields include JSON line N column M. Duplicate names return 409.

CLI super add / super update and the dashboard use the same Manager checks. super check applies the create-body rules to [include] stacks (offline, no daemon) in TOML or legacy JSON.

Get Details

Get full configuration and state for a specific program.

  • GET /api/v1/programs/{id}

{id} is the program UUID (not the name). Resolve it from GET /api/v1/programs.

Update Program

Partially update an existing program. Only fields present in the body are changed; omitted fields are left unchanged.

  • PUT /api/v1/programs/{id}

Body (all fields optional):

{
  "command": "/usr/local/bin/my-app",
  "env": { "LOG_LEVEL": "debug" },
  "autorestart": "unexpected",
  "health_check": { "type": "http", "url": "http://127.0.0.1:8080/health" }
}
FieldDescription
name, command, args, cwd, user, groupProgram identity and execution
env, env_fileEnvironment (env_file = "" clears)
autostart, retry_limit, autorestart, exitcodes, startsecs, stopsecs, priorityRestart / stop behaviour
depends_on, health_check, hooksOrchestration
stdout_logfile, stderr_logfileCustom log paths (must resolve under storage.log_dir)
artifactOTA binary update — see below
cronCron expression — see Scheduled Tasks.
on_overlapskip (default) / queue / kill — cron overlap policy
catchupskip (default) / latest / all — missed-slot backfill policy
jitter_secMax random delay (seconds) before each cron trigger
max_concurrentMax overlapping cron runs at once (default 1, 1–64)
max_queuedCap on queued cron firings when at max_concurrent (default 100; 0 = default) — firings beyond the cap are dropped and recorded as queue_full events
resource_limits💎 Requires isolation plugin on Linux — stored in config always; enforced only when plugin is loaded
sourceProvenance label of the last write path (cli:add, cli:update, stack:<file>, include:<file>, import:<format>, or any caller-supplied value, max 128 chars, no control characters). Empty string clears it; omit for no change. Shown by super info and in the program detail response (config.source); not part of the list summary.

Update persists config only

Updating command, env, etc. persists config only — it does not restart a running process. Call POST /api/v1/programs/{id}/restart explicitly, or change artifact.checksum to trigger an automatic OTA restart.

OTA update via API

When artifact.checksum differs from the stored value, Super starts the transactional OTA flow (download → verify → backup → swap → restart → health validate / rollback). See Atomic OTA Updates.

Step 1 — resolve UUID:

curl -s http://127.0.0.1:9002/api/v1/programs \
  | jq -r '.[] | select(.name=="my-app") | .id'

Step 2 — trigger update:

curl -X PUT "http://127.0.0.1:9002/api/v1/programs/${PROGRAM_ID}" \
  -H "Content-Type: application/json" \
  -d '{
    "artifact": {
      "source": "https://example.com/builds/v2.0.0/app-linux-amd64",
      "checksum": "a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789",
      "destination": "/usr/local/bin/my-app",
      "extract": false,
      "restart_policy": "immediate",
      "download_timeout": 60,
      "verify_timeout": 60
    }
  }'
artifact fieldDescription
sourceDownload URL (HTTPS; HTTP only on loopback)
checksumExpected SHA256 hex of the downloaded bytes
destinationAbsolute path of the final binary on disk
extracttrue to unpack .tar.gz / .tgz / .tar / .zip and stage one payload file (default false)
restart_policyimmediate (default), manual, signal, or signal:<hup|int|term|quit|usr1|usr2>. signal* requires an enabled health_check.
download_timeoutMax seconds for this download (default 60). 0 disables the overall transfer deadline (connect still times out at 10s).
verify_timeoutPost-swap health window before auto-rollback (default 60). 0 disables. Per-program — not a super.toml / [server] setting.

Full schema: Config reference — artifact. Flow: Atomic OTA Updates.

Auth: When core auth is active (auth_secret set) or the security plugin is loaded, add -H "Authorization: Bearer <secret-or-token>". /health stays open without a Bearer.

Response: 200 OK on success; 400 if the program is not found or validation fails. The message names the field (and program '…' when known). JSON syntax / unknown fields include JSON line N column M.

Control Actions

Perform lifecycle actions.

  • POST /api/v1/programs/{id}/start
  • POST /api/v1/programs/{id}/stop (Query param: ?force=true)
  • POST /api/v1/programs/{id}/restart

Historical Logs

Read the last N lines from on-disk log files ({uuid}.out / {uuid}.err).

  • GET /api/v1/programs/{id}/logs

Query parameters:

ParamDefaultDescription
tail200Lines from end of file (max 5000)
sourcebothstdout or stderr

Response:

{
  "id": "a1b2c3d4-...",
  "logs": [
    { "source": "stdout", "content": "line-1\nline-2\n" },
    { "source": "stderr", "content": "error line\n" }
  ]
}

Event History

Read a program’s persisted event history (data/events.db, SQLite). Default sort is time ascending; pass order=desc and/or sort_by=… to change. All lifecycle events are recorded — not just anomalies. Optional query filters are combinable. For a user-oriented walkthrough of storage, retention, and querying, see Event History.

  • GET /api/v1/programs/{id}/events
Query paramTypeDescription
fromintInclusive lower bound on ts (Unix seconds)
tointInclusive upper bound on ts (Unix seconds)
event_typestringExact event type (e.g. process_fatal, cron_exit)
exit_codeintExact exit code
qstringFree-text match on msg
limitintMax rows
offsetintPagination offset
sort_bystringtime (default) · event · exit_code · signal · retry_count · duration_secs · msg
orderstringasc (default) or desc
  • GET /api/v1/events — same filters, plus program_id to scope to one program (omit for the whole daemon).
  • GET /api/v1/events/stats?program_id=<uuid> — retention statistics: total, by_type (count per event type), first_ts/last_ts (retained time range).

Response: array of event records:

[
  {
    "ts": 1760000000,
    "ts_ms": 1760000000123,
    "program_id": "2d4c3f1e-...",
    "program_name": "web",
    "event": "process_fatal",
    "exit_code": null,
    "signal": 9,
    "retry_count": 3,
    "duration_secs": null,
    "msg": "Stopped after 3 retries. Last exit code: None"
  }
]
FieldDescription
tsUnix timestamp (seconds)
ts_msUnix timestamp (milliseconds) — precise time point, stable ordering
program_idOwning program id (null for system-wide events)
program_nameOwning program name (null for system-wide events)
eventprocess_fatal · process_backoff · process_recovered · process_exit · health_restart · cron_started · cron_exit · cron_spawn_failed · queue_full · system_startup · system_shutdown
exit_codeProcess exit code, when captured
signalTerminating signal (e.g. 9 = SIGKILL, includes cgroup OOM kills)
retry_countBackoff retry counter (fatal/backoff only)
duration_secsExecution duration in seconds (cron runs only)
msgHuman-readable detail

Events survive superd restarts. Default retention is 30 days ([storage] events_keep_days); set 0 to keep everything. Pruning runs once per day.

Send Signal

  • POST /api/v1/programs/{id}/signal

Body:

{
  "signal": "hup"
}

System & Stack

Apply Stack (Declarative)

Update the entire system state to match a stack definition. The request body is JSON or TOML — both use the same schema as stack files (see Declarative Stacks), selected by Content-Type.

  • PUT /api/v1/stack

Response: 200 OK with apply log lines. Invalid body or program bodies return 400; message names services[i] (name=…) and the field, or path:line:col: / JSON line N column M for parse errors.

JSON body (default)

JSON is the default body type: omit Content-Type or send application/json. Plain curl -d works — a single-line JSON document is valid. Example file stack.json:

{
  "prune": true,
  "services": [ ... list of program configs ... ]
}
curl -X PUT http://prod-server:9002/api/v1/stack \
  -H "Authorization: Bearer $TOKEN" \
  -d @stack.json

TOML body

TOML requires Content-Type: application/toml (or text/toml). Use --data-binary — plain -d strips newlines and breaks multi-line TOML. Example file stack.toml:

# stack.toml
prune = true

[[services]]
name = "web"
command = "/bin/true"
curl -X PUT http://prod-server:9002/api/v1/stack \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/toml" \
  --data-binary @stack.toml

Shutdown

Gracefully stop the daemon (same for foreground and superd --daemon instances).

  • POST /api/v1/system/shutdown

System Stats

Host-level CPU and memory snapshot (refreshed every ~3s by the monitor thread).

  • GET /api/v1/system/stats

Response:

{
  "cpu_percent": 12.4,
  "memory_used_bytes": 4294967296,
  "memory_total_bytes": 17179869184,
  "timestamp": 1719820800
}

Observability

Prometheus Metrics

Export metrics in Prometheus text format.

  • GET /metrics — Prometheus text format. Requires Bearer when API auth is enabled (same credential as other APIs).

Log Stream (WebSocket)

Stream stdout/stderr.

  • WS /ws?id={program_id}

Batch Operations

Perform actions on multiple programs simultaneously.

  • POST /api/v1/programs/batch

Body:

{
  "target_ids": ["uuid-1", "uuid-2"], // Or omit and use "group_name": "backend"
  "select_all": false,
  "action": {
    "type": "Restart",
    "payload": null
  }
}

The action is a tagged union (type + payload). Payload shape varies by action:

ActionPayload
Start / Restart / Removenull
Stop{ "force": false }
Signal{ "signal": "term" } (one of hup, int, term, kill, quit, usr1, usr2)

Example — stop several programs:

{
  "target_ids": ["uuid-1", "uuid-2"],
  "select_all": false,
  "action": { "type": "Stop", "payload": { "force": false } }
}

Authentication routes

Core auth (OSS) — login / logout / status

When core auth is active, OSS registers:

  • POST /api/v1/auth/login — Bearer must match the admin secret
  • POST /api/v1/auth/logout
  • GET /api/v1/auth/status — capability discovery (auth-exempt)

Without core auth and without the security plugin, these routes are not registered (404).

Access Tokens & secret policy 💎 (security plugin)

Warning

Token CRUD and auth_secret disable require the licensed security plugin. Without it those routes return 404. When the plugin is loaded it replaces the OSS core auth gate (one middleware).

Bootstrap with config auth_secret; Admins may optionally disable it after creating an Admin token. See Authentication.

  • POST /api/v1/auth/login
  • POST /api/v1/auth/logout
  • GET /api/v1/auth/status
  • POST /api/v1/auth/secret/disable 💎
  • GET /api/v1/auth/tokens 💎
  • POST /api/v1/auth/tokens 💎
  • POST /api/v1/auth/tokens/{id}/renew 💎
  • DELETE /api/v1/auth/tokens/{id} 💎

Create Token body 💎:

{
  "name": "ci-deploy-bot",
  "role": "operator"
}

System Configuration (licensed plugins 💎)

Note

License route is served by OSS core when a valid [license].key is configured at startup.
Notify routes require the notify plugin; without it they return 404 Not Found.

Authentication: When core auth or the security plugin is active, protected routes (including license) require a valid Bearer — same as other authenticated API calls.

Get License Info

  • GET /api/v1/system/license

Returns verified subscription metadata plus runtime plugin versions (versions are not part of the signed license claims).

Auth: Required when core auth or the security plugin is active (Authorization: Bearer <secret-or-token>).

Response 200:

{
  "issued_to": "Customer Name",
  "issued_at": 1710000000,
  "major_version": 1,
  "grants": ["security", "notify", "ui"],
  "expires_at": 1741536000,
  "license_id": "550e8400-e29b-41d4-a716-446655440000",
  "features": ["auth", "notify", "dashboard"],
  "plugin_versions": {
    "security": "0.1.0",
    "ui": "0.1.0"
  }
}
FieldNotes
expires_atOmitted when license is perpetual (no expiry in claims)
featuresUI feature codes derived from authorized grants[]
plugin_versionsLoaded plugin crate versions at runtime

Errors: 404 if no license configured; 401 if auth required and missing/invalid token.

Manage Notifications

View or hot-reload webhook channels.

  • GET /api/v1/system/notify
  • PUT /api/v1/system/notify
  • POST /api/v1/system/notify/test

Was this page helpful? Thanks for your feedback!

Still have questions? Open an issue or browse the source.