oxid/ docs

// 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.

EndpointDescription
POST/GET /api/v1/projectsRegister (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}/deployDeploy a branch. Returns the environment, or {"status":"queued"}.
POST /api/v1/projects/{id}/rollbackRedeploy at a prior commit.
GET /api/v1/projects/{id}/environmentsEnvironments 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|wakeScale-to-zero controls.
GET /api/v1/environments/{id}/logsLog snapshot; /logs/stream is the SSE live tail.
GET /api/v1/environments/{id}/auditFull 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/auditCross-project audit feed (?project_id&branch&since&until&kind&limit).
GET /api/v1/queue · GET /api/v1/statsDeploy 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-keyZero-downtime master key rotation.
GET /api/v1/backup, POST /api/v1/backup/restoreSnapshot download; staged restore (needs OXID_ALLOW_RESTORE=1).
GET /api/v1/infra/status, POST /api/v1/infra/bootstrapTraefik/network wiring, inspect and idempotent setup.
GET /api/v1/nodes · POST /api/v1/nodesThe 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/configTraefik'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/statusPublic onboarding probe — version, auth_required, auto_token, webhook_secret_configured. No auth needed.
GET /api/v1/setup/webhook-secretReveal webhook secret (master token required) — same trust level as backup download.
GET /api/v1/setup/tokenHands 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/healthLiveness 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.