// http api
Authentication
Every endpoint lives under /api/v1. When the daemon has
OXID_API_TOKEN set, all of them require
Authorization: Bearer <token> — except the deliberately open ones:
/api/v1/health, /api/v1/setup/status (public onboarding probe),
the four /api/v1/webhooks/* endpoints (signature-verified instead),
/api/v1/setup/token (auto-generated token only) and the Traefik-facing
/api/v1/wake + /api/v1/heartbeat.
Named tokens minted with oxid token create
work here too — hand each integration its own so revocation doesn't mean rotating the
master credential.
Endpoints
The CLI and dashboard are thin clients over exactly this surface — anything they do, you can do with curl.
| Endpoint | Description |
|---|---|
POST/GET /api/v1/projects | Register (idempotent) / list projects. POST accepts {"repo_dir": "/repos/app"} or {"repo_url": "https://…", "git_token": "…"} (the daemon clones itself; scp-style git@host:org/repo.git normalized). |
PATCH/DELETE /api/v1/projects/{id} | Update lifetime policy / git token; delete project. |
POST /api/v1/projects/{id}/deploy | Deploy a branch. Returns the environment, or {"status":"queued"}. |
POST /api/v1/projects/{id}/rollback | Redeploy at a prior commit. |
GET /api/v1/projects/{id}/environments | Environments of a project (filterable by ?branch=). |
GET /api/v1/environments/{id} | One environment by id. |
DELETE /api/v1/environments/{id} | Destroy (?purge_secrets=true optional). |
POST /api/v1/environments/{id}/pause|wake | Scale-to-zero controls. |
GET /api/v1/environments/{id}/logs | Log snapshot; /logs/stream is the SSE live tail. |
GET /api/v1/environments/{id}/audit | Full history of one environment. |
GET/POST /api/v1/secrets, DELETE /api/v1/secrets/{name} | Global secrets (same shape under /projects/{id}/secrets). |
GET /api/v1/audit | Cross-project audit feed (?project_id&branch&since&until&kind&limit). |
GET /api/v1/queue · GET /api/v1/stats | Deploy queue; host + environment stats for the dashboard. |
POST/GET /api/v1/tokens, DELETE /api/v1/tokens/{id} | Named API token management (master token required). |
POST /api/v1/rotate-key | Zero-downtime master key rotation. |
GET /api/v1/backup, POST /api/v1/backup/restore | Snapshot download; staged restore (needs OXID_ALLOW_RESTORE=1). |
GET /api/v1/infra/status, POST /api/v1/infra/bootstrap | Traefik/network wiring, inspect and idempotent setup. |
GET /api/v1/nodes · POST /api/v1/nodes | The fleet: list, and register (or correct) a node. The connection is probed before the row is written. |
PATCH /api/v1/nodes/{id} | {"state": "draining"} stops new placements; add "evacuate": true to also move every live branch off by redeploying it elsewhere. down is refused — that is what a failed health probe records. |
DELETE /api/v1/nodes/{id} | Retire a node. 409 while anything still references it, destroyed environments included. |
GET /api/v1/environments?node={id} | Every live environment across every project, optionally on one node — what backs oxid node ls --envs. Node-wide by construction, so a project-scoped credential is refused. Historical rows are collapsed to the live deploy per branch. |
GET /api/v1/traefik/config | Traefik's HTTP provider polls this for a router per environment, built from the database — including environments on other nodes and ones whose container is stopped, neither of which the Docker label provider can see. Authenticated and ETagged. |
GET /api/v1/setup/status | Public onboarding probe — version, auth_required, auto_token, webhook_secret_configured. No auth needed. |
GET /api/v1/setup/webhook-secret | Reveal webhook secret (master token required) — same trust level as backup download. |
GET /api/v1/setup/token | Hands over the auto-generated master token so a fresh install can self-serve. Governed by OXID_BOOTSTRAP_TOKEN_ACCESS: loopback (default) serves only callers on the daemon's own host, judged on the connection's real peer address and never on X-Forwarded-For; any serves anyone who can reach the port, for a containerized daemon whose port is published privately (what the shipped compose does); off disables it. Also 404 when the token was set explicitly: it never reveals one you chose. |
GET /api/v1/health | Liveness plus the daemon version. Always open. |
POST /api/v1/webhooks/{github|gitlab|gitea|gogs} | Push webhooks, one per provider. Answered 202 {"status":"queued"}; the deploy runs off the persisted queue. See the guide for each provider's verification header. |
Errors come back as {"error": "..."} with a meaningful status — the CLI
maps 404 → exit code 2, 401/403 → 4, everything else → 1.