API reference

kimbap's REST API is served under /api/, using stdlib net/http routing (no framework). Every endpoint returns JSON. Auth is either a session cookie (set by /api/auth/login, used by the web UI) or an Authorization: Bearer <key> header (an api- or both-scoped API key).

Auth

Method & path Auth Description
GET /api/setup/status none {"needsSetup": bool} — whether the first-admin wizard should show.
POST /api/setup/admin none (only while no user exists) Create the first admin account + log in.
POST /api/auth/login none {username, password} → sets session cookie.
POST /api/auth/logout session Revoke the current session.
GET /api/auth/me session or key Current user.

Users & API keys

Method & path Auth Description
GET /api/users admin List users.
POST /api/users admin Create a user {username, password, role}.
GET /api/users/{id}/apikeys self or admin List a user's API keys.
POST /api/users/{id}/apikeys self or admin Create a key {label, scope} (scope = api|mcp|both). Raw key returned once.
DELETE /api/apikeys/{id} self (owner) or admin Revoke a key.

Projects

Method & path Description
GET /api/projects List every project.
POST /api/projects Create {slug, name, description, compose, env}.
GET /api/projects/{slug} Details + current compose/env content + live container status.
PUT /api/projects/{slug} Overwrite {compose, env}. Doesn't redeploy.
DELETE /api/projects/{slug} Stop, tear down, and delete the project.
POST /api/projects/{slug}/deploy docker compose up -d.
POST /api/projects/{slug}/stop docker compose stop.
POST /api/projects/{slug}/restart docker compose restart.
GET /api/projects/{slug}/logs?service=&tail= Recent logs.
GET /api/projects/{slug}/logs/stream?service=&tail= Live-tailing logs (docker compose logs -f) as a chunked text response — ends when the client disconnects or after 30 minutes.

Domains

Unavailable if Traefik is disabled (KIMBAP_TRAEFIK_ENABLED=false — see Domains & TLS): POST returns 400. GET/DELETE still work either way, so existing rows can always be inspected/cleaned up.

Method & path Description
GET /api/projects/{slug}/domains List a project's domains.
POST /api/projects/{slug}/domains Add {service, domain, containerPort, tls}. 400 if Traefik is disabled.
DELETE /api/projects/{slug}/domains/{id} Remove a domain.

Deploy tokens & trigger

See the Trigger guide for the full mechanism.

Method & path Auth Description
GET /api/deploy-tokens session or key List every deploy token across every project (each entry includes its project's slug/name).
GET /api/projects/{slug}/deploy-tokens session or key List one project's deploy tokens.
POST /api/projects/{slug}/deploy-tokens session or key Create {label}. Raw token returned once.
DELETE /api/projects/{slug}/deploy-tokens/{id} session or key Revoke a deploy token.
POST /api/deploy/{slug} deploy token (Authorization: Bearer kbpdeploy_...) — not a session or API key Redeploy the project, optionally {"env": {"KEY": "value"}} to merge those values into .env first (existing keys not mentioned are untouched).

System

Method & path Auth Description
GET /api/system/info session or key This kimbap: version, os, arch, goVersion, stateDir, adminDomain, traefikEnabled, startedAt.
GET /api/system/info/latest session or key Asks the release site for the newest version: {current, latest, upToDate, updatable}. Makes an outbound request, so call it deliberately, not on a poll. 502 if the site is unreachable.
GET /api/system/status session or key Docker + Traefik status, plus traefikEnabled (config.Config.TraefikEnabled) — traefik is a zero value when false.
POST /api/system/docker/install admin {"confirm": true} — install/update Docker (streamed response).
POST /api/system/traefik/provision admin {"confirm": true} — (re)provision Traefik (streamed response). 400 if Traefik is disabled.

Backup

Admin-only on both sides: an export carries every project's .env in plain text, and an import can replace existing projects. Both are audit-logged. See Backup & migration for what an archive does and doesn't contain.

Method & path Auth Description
GET /api/system/backup/export admin Every project as a tar.gz (Content-Disposition: attachment).
GET /api/projects/{slug}/backup/export admin One project, same format.
POST /api/system/backup/inspect admin multipart/form-data with an archive file. Returns the manifest plus, per project, whether that slug already exists here. Writes nothing.
POST /api/system/backup/import admin Same archive field, plus a decisions field: a JSON object mapping slug → "import", "overwrite", or "skip". Anything not named is skipped. Returns a per-project report and needsDeploy — importing never starts containers.

Volumes

Listing volumes is ordinary read access. Everything that reads or writes their contents is admin-only and audit-logged: a snapshot is a verbatim copy of a project's data, and restoring one erases a volume with no undo. See Volumes & snapshots.

Snapshots and restores are long-running, so they return 202 with an operation to poll rather than blocking. Only one runs at a time on a host — a second request gets 409.

Method & path Auth Description
GET /api/volumes?project= session or key Volumes belonging to kimbap's projects, optionally filtered to one.
GET /api/volumes/usages session or key {name: {bytes, links}} for every volume. Docker computes this by walking them all — seconds, not milliseconds, and bytes is rounded. Call it on demand, not on a poll.
GET /api/volumes/storage session or key {snapshotBytes, freeBytes, freeKnown, active?}. active is the running operation, if any. freeKnown is false where the platform can't report free space.
GET /api/volumes/{name}/containers session or key Containers referencing the volume, including stopped ones.
POST /api/volumes/{name}/snapshots admin {"stopContainers": bool} → 202 + operation. With stopContainers, the project is stopped for the copy and started again afterwards. Without it the snapshot is marked hot.
GET /api/volume-snapshots?volume= admin The snapshot catalogue, newest first.
POST /api/volume-snapshots?volume=&project=&logicalName= admin Register an uploaded archive. The body is the tar.gz itself, not a multipart form — these run to gigabytes. Rejected unless it reads as a valid archive.
POST /api/volume-snapshots/{id}/restore admin {"volume": ""} (empty = the volume it came from) → 202 + operation. Verifies the archive before stopping or erasing anything.
GET /api/volume-snapshots/{id}/download admin The archive, as tar.gz.
DELETE /api/volume-snapshots/{id} admin Delete the archive and its catalogue entry. The volume is untouched.
GET /api/volume-operations/{id} admin Poll a snapshot/restore: {status, message, error}. Operations are forgotten an hour after they finish, and don't survive a kimbap restart — the snapshot they produce does.

Registries

Method & path Auth Description
GET /api/system/registries admin List saved registry credentials (registry + username only, never the password).
POST /api/system/registries admin Add {registry, username, password} — verified with a real docker login before it's saved.
DELETE /api/system/registries/{id} admin Remove a credential (and docker logout that registry, best-effort).

Audit

Method & path Auth Description
GET /api/audit?limit= admin Recent audit log entries.

Errors

Errors are {"error": "message"} with a matching HTTP status — 400 for bad input, 401 for missing/invalid auth, 403 for a role/ownership check that failed, 404 for a missing resource, 409 for a conflict (e.g. duplicate slug or domain), 422 for a lifecycle action that ran but failed (e.g. docker compose up exiting non-zero — the response includes the command's output), 500 for anything unexpected (internals are never leaked in the message).

MCP

Everything above except user/API-key management is also available as MCP tools at /mcp — see the MCP guide for the full tool list and how to connect an AI agent (Claude, ChatGPT, or any other MCP-compatible client) to it.