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.