API Endpoints¶
All HTTP and WebSocket endpoints, alphabetized by path. All REST paths
are under /api/v1 except /health.
Auth types:
- None — public, no credentials required
- JWT —
Authorization: Bearer <access_token>(user session) - ACL — JWT + permission check on a resource (e.g.
viewon/workspaces/{id}) - Workspace JWT —
Authorization: Bearer <workspace_token>(container→host)
Endpoints¶
DELETE /api/v1/admin/groups/{id}¶
Delete a group (admin).
Auth: JWT required. User must have admin permission on /.
No request body.
DELETE /api/v1/admin/groups/{id}/members/{user_id}¶
Remove a user from a group (admin).
Auth: JWT required. User must have admin permission on /.
No request body.
DELETE /api/v1/admin/invitations/{id}¶
Revoke a pending invitation.
Auth: JWT required. User must have admin permission on /.
No request body.
DELETE /api/v1/admin/users/{id}¶
Delete a user account. Cannot delete self or the system agent user.
Auth: JWT required. User must have admin permission on /.
No request body.
GET /api/v1/admin/acl/by-principal/group/{id}¶
List all ACL entries granted to a specific group across all resources.
Auth: JWT required. User must have admin permission on /.
No request body.
[
{
"resource": "/workspaces/uuid",
"action": 1,
"principal_type": 2,
"permission": "view",
"group_id": "uuid"
}
]
GET /api/v1/admin/acl/by-principal/user/{id}¶
List all ACL entries granted to a specific user across all resources.
Auth: JWT required. User must have admin permission on /.
No request body.
[
{
"resource": "/workspaces/uuid",
"action": 1,
"principal_type": 1,
"permission": "terminal",
"user_id": "uuid"
}
]
GET /api/v1/admin/acl/resource¶
Get the ACL entries for a specific resource. Query param: resource
(e.g. /workspaces/uuid).
Auth: JWT required. User must have admin permission on the
requested resource.
No request body.
[
{
"resource": "/workspaces/uuid",
"action": 1,
"principal_type": 1,
"permission": "view",
"user_id": "uuid",
"group_id": null,
"system_principal": null
}
]
GET /api/v1/admin/acl/tree¶
Get a summary of the entire ACL tree across all resources.
Auth: JWT required. User must have admin permission on /.
No request body.
[
{ "resource": "/workspaces/uuid", "ace_count": 3 },
{ "resource": "/groups/uuid", "ace_count": 1 }
]
GET /api/v1/admin/groups¶
List all groups in the system (admin).
Auth: JWT required. User must have admin permission on /.
No request body.
GET /api/v1/admin/groups/{id}/members¶
List members of a group (admin).
Auth: JWT required. User must have admin permission on /.
No request body.
GET /api/v1/admin/invitations¶
List all invitations (pending, accepted, and revoked).
Auth: JWT required. User must have admin permission on /.
No request body.
[
{
"id": "uuid",
"email": "invited@example.com",
"invited_by": "inviter-uuid",
"invited_by_email": "admin@example.com",
"status": "pending",
"created_at": "2025-01-01 12:00:00",
"accepted_at": null
}
]
GET /api/v1/admin/users¶
List all user accounts in the system.
Auth: JWT required. User must have admin permission on /.
No request body.
[
{
"id": "uuid",
"email": "user@example.com",
"verified": true,
"provider": "local",
"created_at": "2025-01-01 12:00:00",
"groups": [{ "id": "uuid", "name": "admins" }]
}
]
GET /api/v1/auth/me¶
Get the current authenticated user's profile.
Auth: JWT required.
No request body.
GET /api/v1/auth/oidc/{provider_id}/callback¶
OIDC callback endpoint. Called by the identity provider after authentication. Validates the state cookie and exchanges the authorization code for tokens.
Auth: None. Query params: code, state, optional error.
Returns HTTP 302 redirect to /#/oidc-complete?token=... or CLI localhost URL.
GET /api/v1/auth/oidc/{provider_id}/login¶
Initiate OIDC login. Redirects the user to the identity provider's authorization endpoint.
Auth: None. Optional query param: cli_redirect (must be localhost URL).
Returns HTTP 302 redirect to OIDC IdP.
GET /api/v1/auth/verify¶
Verify a user's email address using the token from the verification email. Returns a session token on success.
Auth: None. Query param: token (verification JWT from email link).
GET /api/v1/auth/verify-workspace-token¶
Validate a workspace JWT. Used internally by nginx auth_request to
gate container-to-host traffic.
Auth: Workspace JWT required (Authorization: Bearer <workspace_token>).
On failure: 401 with X-Token-Error header (missing, expired, or invalid).
GET /api/v1/config¶
Get public instance configuration: whether registration and invitations are enabled, available OIDC providers, login banner text, and plugin frontend config.
Auth: None.
No request body.
{
"registration_enabled": true,
"invitations_enabled": true,
"login_banner_title": "",
"login_banner": "",
"oidc_providers": [],
"auth_modes": [],
"instance_id": "string"
}
GET /api/v1/groups¶
List all groups visible to the current user.
Auth: JWT required.
No request body.
GET /api/v1/groups/{id}/members¶
List the members of a group.
Auth: JWT required. User must have view permission on /groups/{id}.
No request body.
GET /api/v1/images¶
List available container images that can be used when creating or editing workspaces.
Auth: JWT required.
No request body.
GET /api/v1/my-permissions¶
Get the current user's effective permissions. If a resource query param
is provided, returns permissions for that specific resource; otherwise
returns permissions across all static resources.
Auth: JWT required. Optional query param: resource.
No request body.
{
"user_id": "uuid",
"email": "user@example.com",
"groups": [],
"permissions": {
"/workspaces/uuid": ["view", "terminal", "files", "chat"]
}
}
GET /api/v1/users/search¶
Search for users by email or handle. Used for autocomplete when sharing workspaces or adding group members.
Auth: JWT required. Query param: q (search string, min length 1).
No request body.
GET /api/v1/version¶
Get the build version, git commit, build timestamp, and list of installed plugins.
Auth: None.
No request body.
GET /api/v1/volumes¶
List podman volumes owned by the current user.
Auth: JWT required.
No request body.
GET /api/v1/workspaces¶
List workspaces owned by the current user.
Auth: JWT required.
No request body.
[
{
"id": "uuid",
"name": "my-workspace",
"container_id": null,
"image": null,
"default_command": null,
"mounts": null,
"env": null,
"created_at": "2025-01-01 12:00:00"
}
]
GET /api/v1/workspaces/shared¶
List workspaces that other users have shared with the current user.
Auth: JWT required.
No request body.
[
{
"id": "uuid",
"name": "shared-workspace",
"container_id": null,
"image": null,
"default_command": null,
"mounts": null,
"env": null,
"created_at": "2025-01-01 12:00:00",
"owner_email": "owner@example.com"
}
]
GET /api/v1/workspaces/{id}/acl¶
Get the resolved ACL entries for a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
[
{
"resource": "/workspaces/uuid",
"action": 1,
"principal_type": 1,
"permission": "view",
"user_id": "uuid",
"group_id": null,
"system_principal": null
}
]
GET /api/v1/workspaces/{id}/export¶
Export a workspace as a .tar.gz archive. The archive contains the
workspace configuration and container filesystem.
Auth: JWT required. User must have admin permission on the
requested resource.
No request body. Returns StreamingResponse (.tar.gz binary stream).
Headers: Content-Disposition: attachment; filename="<name>.tar.gz",
X-Estimated-Size: <bytes>.
GET /api/v1/workspaces/{id}/files¶
List files and directories inside the workspace container.
Auth: JWT required. User must have files permission on
/workspaces/{id}. Query param: path (default .).
No request body.
[
{
"name": "README.md",
"path": "work/README.md",
"is_dir": false,
"size": 1024,
"mtime": 1704067200.0,
"ctime": 1704067200.0
}
]
GET /api/v1/workspaces/{id}/files/content¶
Read the contents of a file inside the workspace container.
Auth: JWT required. User must have files permission on
/workspaces/{id}. Query param: path.
No request body.
GET /api/v1/workspaces/{id}/files/download¶
Download a file or directory from the workspace container. Single files are returned directly; directories are zipped.
Auth: JWT required. User must have files permission on
/workspaces/{id}. Query param: path.
No request body. Returns FileResponse (single file) or .zip archive
(directory).
GET /api/v1/workspaces/{id}/groups¶
List groups that have been granted access to a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
GET /api/v1/workspaces/{id}/members¶
List individual users who have been granted access to a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
GET /api/v1/workspaces/{id}/roles¶
List role groups for a workspace and their members. Each workspace has
four roles: owners, coders, collaborators, spectators.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
[
{
"role": "owners",
"group_id": "uuid",
"group_name": "owners-uuid",
"members": [{ "id": "uuid", "email": "user@example.com" }]
}
]
PATCH /api/v1/admin/groups/{id}¶
Update a group's name or description (admin).
Auth: JWT required. User must have admin permission on /.
PATCH /api/v1/admin/users/{id}¶
Update a user's email, password, or handle (admin). All fields optional.
Auth: JWT required. User must have admin permission on /.
PATCH /api/v1/groups/{id}¶
Update a group's name or description.
Auth: JWT required. User must have edit permission on /groups/{id}.
PATCH /api/v1/workspaces/{id}/roles¶
Change a user's role in a workspace. Set role to null to remove the
user from all roles.
Auth: JWT required. User must have share permission on /workspaces/{id}.
POST /api/v1/admin/groups¶
Create a new group (admin).
Auth: JWT required. User must have admin permission on /.
POST /api/v1/admin/groups/{id}/members¶
Add a user to a group (admin).
Auth: JWT required. User must have admin permission on /.
POST /api/v1/admin/invitations¶
Send an invitation email to a new user.
Auth: JWT required. User must have admin permission on /.
POST /api/v1/admin/invitations/{id}/resend¶
Resend an invitation email.
Auth: JWT required. User must have admin permission on /.
No request body.
POST /api/v1/admin/users¶
Create a new user account. By default the user is created verified with
the given password. Set send_verification_email to true to create
the user unverified and send a verification email so they can set their
own password (the password field is ignored in this case).
Auth: JWT required. User must have admin permission on /.
With password (default):
With verification email:
POST /api/v1/auth/accept-invite¶
Accept an invitation and create a new account. The token is from the invitation email.
Auth: None.
POST /api/v1/auth/change-email¶
Change the current user's email address. Requires the current password for verification. The account is marked unverified until the new email is confirmed.
Auth: JWT required.
POST /api/v1/auth/change-handle¶
Change the current user's display handle. Requires the current password for verification.
Auth: JWT required.
POST /api/v1/auth/change-password¶
Change the current user's password. Requires the current password.
Auth: JWT required.
POST /api/v1/auth/forgot-password¶
Request a password reset email. Always returns success even if the email doesn't exist (prevents user enumeration). 60s rate limit per email.
Auth: None.
POST /api/v1/auth/login¶
Authenticate with email and password. Returns a JWT access token.
Auth: None. Rate-limited (see Rate Limiting below).
POST /api/v1/auth/logout¶
Log out the current session. Blocklists the token's JTI so it cannot be reused.
Auth: JWT required.
No request body.
For OIDC users with logout redirect configured:
POST /api/v1/auth/refresh¶
Exchange the current JWT for a new one. The old token's JTI is blocklisted.
Auth: JWT required.
No request body.
POST /api/v1/auth/register¶
Create a new user account. A verification email is sent unless running
in test mode. Can be disabled via KLANGK_DISABLE_REGISTRATION.
Auth: None.
With email verification:
In test mode (auto-verified):
POST /api/v1/auth/resend-verification¶
Resend the email verification link. Requires the account password. 60s rate limit per email.
Auth: None.
POST /api/v1/auth/reset-password¶
Set a new password using the token from a password reset email. Returns a session token (auto-login after reset).
Auth: None.
POST /api/v1/browser-delegate¶
Relay a request from a workspace container to a connected browser tab. Used by Pi extensions that need to interact with the user's browser (e.g. navigating, reading page content).
Auth: Workspace JWT required + nginx IP ACL (container traffic only).
Returns forwarded result from the target browser tab (arbitrary JSON).
POST /api/v1/browser-delegate/stream¶
Streaming variant of browser-delegate. Returns results as newline- delimited JSON chunks.
Auth: Workspace JWT required + nginx IP ACL (container traffic only).
Returns StreamingResponse (application/x-ndjson).
POST /api/v1/groups¶
Create a new group.
Auth: JWT required. User must have create permission on /groups.
POST /api/v1/groups/{id}/members¶
Add a user to a group.
Auth: JWT required. User must have manage_members permission on
/groups/{id}.
POST /api/v1/volumes¶
Create a new podman volume labeled with the current user's ID.
Auth: JWT required.
POST /api/v1/workspaces¶
Create a new workspace. The creating user becomes the owner with full ACL permissions. Role groups (owners, coders, collaborators, spectators) are created automatically.
Auth: JWT required.
{
"name": "my-workspace",
"image": "klangk-workspace:latest",
"default_command": "/bin/bash",
"mounts": ["my-volume:/home/user/data"],
"env": { "MY_VAR": "value" }
}
All fields except name are optional.
{
"id": "uuid",
"user_id": "uuid",
"name": "my-workspace",
"image": null,
"default_command": null,
"mounts": null,
"env": null,
"num_ports": 5,
"created_at": "2025-01-01 12:00:00"
}
POST /api/v1/workspaces/import¶
Create a new workspace from a previously exported .tar.gz archive.
Environment variables are sanitized during import.
Auth: JWT required. Multipart form upload: file (.tar.gz archive),
optional name form field.
{
"id": "uuid",
"user_id": "uuid",
"name": "my-workspace",
"image": null,
"default_command": null,
"mounts": null,
"env": null,
"num_ports": 5,
"created_at": "2025-01-01 12:00:00"
}
POST /api/v1/workspaces/post-chat-message¶
Post a chat message from a workspace container to the workspace's chat channel. Used by Pi extensions and tools running inside the container.
Auth: Workspace JWT required + nginx IP ACL (container traffic only).
{
"id": "uuid",
"workspace_id": "uuid",
"sender": "agent",
"sender_id": "agent",
"text": "text of message",
"message_type": 2
}
POST /api/v1/workspaces/{id}/duplicate¶
Clone an existing workspace's configuration into a new workspace.
Auth: JWT required. User must have create permission on
/workspaces/{id}.
{
"id": "uuid",
"user_id": "uuid",
"name": "my-workspace",
"image": null,
"default_command": null,
"mounts": null,
"env": null,
"num_ports": 5,
"created_at": "2025-01-01 12:00:00"
}
POST /api/v1/workspaces/{id}/files/rename¶
Rename or move a file or directory inside the workspace container.
Auth: JWT required. User must have files permission on
/workspaces/{id}.
POST /api/v1/workspaces/{id}/files/upload¶
Upload a file into the workspace container. Default 500 MB limit.
Auth: JWT required. User must have files permission on
/workspaces/{id}. Multipart form: file (upload), optional path
form field.
POST /api/v1/workspaces/{id}/groups¶
Grant a group access to a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
POST /api/v1/workspaces/{id}/members¶
Grant a user access to a workspace. The user receives view, terminal, files, and chat permissions.
Auth: JWT required. User must have share permission on /workspaces/{id}.
POST /api/v1/workspaces/{id}/restart¶
Restart a workspace by stopping and removing its container. The container is recreated on the next connection.
Auth: JWT required. User must have terminal permission on
/workspaces/{id}.
No request body.
POST /api/v1/workspaces/{id}/roles/{role}¶
Add a user to a workspace role. Valid roles: owners, coders,
collaborators, spectators.
Auth: JWT required. User must have share permission on /workspaces/{id}.
PUT /api/v1/admin/acl/resource¶
Replace all ACL entries for a specific resource. Query param: resource.
Auth: JWT required. User must have admin permission on the
requested resource.
[
{
"action": 1,
"principal_type": 1,
"permission": "view",
"user_id": "uuid",
"group_id": null,
"system_principal": null
}
]
action: 0=deny, 1=allow. principal_type: 0=system, 1=user, 2=group.
[
{
"resource": "/workspaces/uuid",
"action": 1,
"principal_type": 1,
"permission": "view",
"user_id": "uuid",
"group_id": null,
"system_principal": null
}
]
PUT /api/v1/workspaces/{id}¶
Update a workspace's configuration (name, container image, default command, volume mounts, environment variables). All fields optional.
Auth: JWT required. User must have edit permission on
/workspaces/{id}.
{
"name": "renamed",
"image": "klangk-workspace:latest",
"default_command": "/bin/bash",
"mounts": ["vol:/data"],
"env": { "KEY": "VALUE" }
}
PUT /api/v1/workspaces/{id}/acl¶
Replace all ACL entries for a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
[
{
"action": 1,
"principal_type": 1,
"permission": "view",
"user_id": "uuid",
"group_id": null,
"system_principal": null
}
]
[
{
"resource": "/workspaces/uuid",
"action": 1,
"principal_type": 1,
"permission": "view",
"user_id": "uuid",
"group_id": null,
"system_principal": null
}
]
DELETE /api/v1/groups/{id}¶
Delete a group.
Auth: JWT required. User must have delete permission on /groups/{id}.
No request body.
DELETE /api/v1/groups/{id}/members/{user_id}¶
Remove a user from a group.
Auth: JWT required. User must have manage_members permission on
/groups/{id}.
No request body.
DELETE /api/v1/volumes/{name}¶
Delete a podman volume. Only the owning user can delete their volumes.
Auth: JWT required. Checks user ownership.
No request body.
DELETE /api/v1/workspaces/{id}¶
Delete a workspace and stop its container.
Auth: JWT required. User must have delete permission on
/workspaces/{id}.
No request body.
DELETE /api/v1/workspaces/{id}/files¶
Delete a file or directory inside the workspace container. Query param:
path.
Auth: JWT required. User must have files permission on
/workspaces/{id}.
No request body.
DELETE /api/v1/workspaces/{id}/groups/{group_id}¶
Revoke a group's access to a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
DELETE /api/v1/workspaces/{id}/members/{member_id}¶
Revoke a user's access to a workspace.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
DELETE /api/v1/workspaces/{id}/roles/{role}/{member_id}¶
Remove a user from a workspace role.
Auth: JWT required. User must have share permission on /workspaces/{id}.
No request body.
GET /health¶
Readiness check. Returns OK if the server is running.
Auth: None.
No request body.
WebSocket /ws¶
Primary WebSocket connection for real-time communication. Handles terminal I/O, chat messages, workspace status updates, and browser delegate events.
Auth: JWT required via ?token= query param.
Close codes: 4001 (missing/invalid token), 4002 (expired token).
Test-Only Endpoints¶
Available only when KLANGK_TEST_MODE is set. No auth required.
GET /api/v1/test/browsers/{id}¶
List browser registrations for a workspace.
GET /api/v1/test/idle-timeout¶
Get the idle timeout for a workspace. Query param: workspace_id.
POST /api/v1/test/set-idle-timeout¶
Override the idle timeout for a workspace (or globally).
GET /api/v1/test/workspace-token/{id}¶
Generate a workspace JWT for testing container-to-host endpoints.
Rate Limiting¶
Login Brute-Force Protection¶
Disabled by default. Configure via environment variables:
KLANGK_LOGIN_LOCKOUT_FAILURES(default0) — attempts before lockout (0 = disabled)KLANGK_LOGIN_LOCKOUT_DURATION(default900) — lockout period in secondsKLANGK_LOGIN_LOCKOUT_WINDOW(default300) — attempt counting window in seconds
Email Rate Limiting¶
- Verification resend: 60s per email (in-memory)
- Password reset: 60s per email (in-memory)