REST API
The HTTP API behind the dashboard — auth model, conventions, and the main endpoint groups.
The dashboard is a thin client over a JSON HTTP API. That API isn't a separately versioned product surface — it exists to serve the SPA — but it's fully usable for automation once you authenticate, using the same endpoints the UI does.
All app-facing routes live under /api/ (unversioned). Requests and responses are JSON. Real-time data (logs, stats, deploy queue) comes over WebSocket rather than REST.
Authentication
There are two ways in:
Session cookies (the dashboard)
POST /api/auth/login with username and password sets a session cookie. If TOTP is enabled,
login returns a "TOTP required" response and you complete it with POST /api/auth/totp. A CSRF
cookie is set alongside the session; mutating cookie-authenticated requests must echo it in the
X-CSRF-Token header (the SPA does this automatically).
API tokens (automation)
For scripts, create an API token in the dashboard (POST /api/auth/api-tokens) — the raw
token is shown once and stored only as a hash. Send it as a bearer header:
curl -H "Authorization: Bearer vac_…" Bearer tokens take precedence over cookies and are exempt from the CSRF dance, which makes them the right choice for programmatic access. Tokens can carry an optional expiry and be revoked at any time.
Sensitive operations — deleting an app, the box-wide reset, disabling 2FA — require a recent step-up re-verification (POST /api/auth/step-up) on top of being authenticated.
First-run setup
On first boot, vac-api writes a one-time setup token (surfaced in the installer summary).
POST /api/setup/admin consumes it to create the first operator account; after that, setup is
closed.
Endpoint groups
A map of the surface, by resource. Standard REST verbs apply (GET list/read, POST create,
PATCH update, DELETE remove).
| Group | Base | Highlights |
|---|---|---|
| Apps | /api/apps | create/list/update/delete; …/export and /api/apps/import for the app spec; …/detect-compose, …/env-example probes. |
| Deployments | /api/apps/{id}/deployments | trigger, list, fetch; …/{did}/rollback, …/{did}/cancel, …/{did}/logs. /api/deployments/active for the queue. |
| Deploy triggers | /api/apps/{id}/triggers | push/tag rules for auto-deploy. |
| Webhook | /api/apps/{id}/webhook | config + secret rotation. Inbound deliveries hit /webhooks/{appID}. |
| Env vars | /api/apps/{id}/env | list (secrets redacted), replace, …/{key}/reveal. |
| Services | /api/apps/{id}/services | list, …/{name} patch (port, health path), start/stop/restart. |
| App lifecycle | /api/apps/{id} | …/start, …/stop, …/restart. |
| SSH keys | /api/apps/{id}/ssh-key | get public key, regenerate, delete; …/test-connection. |
| Domains | /api/domains, /api/apps/{id}/domains | hub + per-app; assign to a service, redirects, …/refresh. |
| Metrics (request rate) | /api/apps/{id}/metrics, /api/host/metrics | per-app, per-service, and box-wide request time series. |
| Backups | /api/apps/{id}/backups | configs, runs, …/run, download. (managed services) |
| Databases | /api/apps/{id}/databases, /api/databases | provision/list/remove; engines + inventory. (managed services) |
| Add-ons | /api/addons | catalog; …/{id}/install. (managed services) |
| Auth | /api/auth/* | login, TOTP, step-up, sessions, API tokens. |
| Notifications | /api/settings/notifications | Discord/Slack config + test. |
| Instance | /api/instance/* | version/info, base domain, DNS check, deploy concurrency, restart control plane, reset. |
| Security | /api/security/* | posture, live traffic, firewall/fail2ban status. |
| Audit | /api/audit | activity feed; …/{id}/revert, …/{id}/diff. |
Real-time streams
Logs, stats, and deploy-queue updates are delivered over WebSocket (authenticated like REST), not polled. The dashboard subscribes to per-resource topics — see Logs & events.
Metrics for Prometheus
A separate GET /metrics endpoint exposes host vitals, per-app stats, deploy counts, and request
aggregates in Prometheus format. It's default-closed: set VAC_METRICS_TOKEN and present it as
a bearer token, or the endpoint returns 404.