Route reference¶
Every route of the Console API, by area, generated from the OpenAPI document the deployment serves at /api/v1/openapi.yaml. The heading of a route gives its method, its path relative to /api/v1 and, in brackets, the role it needs (read from its summary: a summary with no role is open to every logged-in role). Requests and answers are JSON; errors are application/problem+json; every write of an existing resource needs If-Match. Nested objects are shown one level deep: the document has the rest.
Routes a token cannot call (log in and out, the account's own tokens, minting an onboarding token) need the browser session.
agents¶
GET /agents (viewer)¶
List agents
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of AgentSummary, required)name(string, required)display_name(string or null, optional)kind(string or null, required)description(string, required)avatar_url(string or null, optional)model(string or null, optional)environment(string or null, optional)tool_count(integer, required)mcp(array of string, required)current_version(integer, required)used_by(integer, required)updated_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /agents (admin)¶
Create an agent with its first version (admin)
Request body (application/json):
name(string, required)display_name(string or null, optional)kind(string, optional)description(string, optional)version(AgentVersionInput, required): A new agent version. Withoutfrom_versionit describes a whole version (system_promptis required, the rest defaults); withfrom_versionit starts from that stored version and only the fields present in the body change.nullforsystem_prompt,tools,confirmation_policy,produces_branch,skills,mcp_servers,contextoregressmeans "not given": the base version's value, or the default (empty lists,never_confirm,false, allow-all egress) when there is nofrom_version. Forworks_on_branch,modelandenvironment,nullis a value of its own (unset) and is stored as sent; omit the field to keep the base version's.system_prompt(string or null, optional)tools(array of string or null, optional)confirmation_policy(one of "never_confirm", "confirm_risky" or null, optional)produces_branch(boolean or null, optional)works_on_branch(boolean or null, optional)skills(array of string or null, optional)mcp_servers(array of string or null, optional)egress(EgressInput or null, optional)model(string or null, optional)environment(string or null, optional)context(array of map of any or null, optional)note(string, optional)from_version(integer or null, optional)
Response 201 (application/json):
name(string, required)display_name(string or null, optional)kind(string or null, required)description(string, required)avatar_url(string or null, optional)model(string or null, optional)environment(string or null, optional)tool_count(integer, required)mcp(array of string, required)current_version(integer, required)used_by(integer, required)updated_at(string, required)current(AgentVersion or null, required)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
DELETE /agents/{name} (admin)¶
Delete an agent no template or team uses (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 412, 428.
GET /agents/{name} (viewer)¶
One agent with its current version
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)display_name(string or null, optional)kind(string or null, required)description(string, required)avatar_url(string or null, optional)model(string or null, optional)environment(string or null, optional)tool_count(integer, required)mcp(array of string, required)current_version(integer, required)used_by(integer, required)updated_at(string, required)current(AgentVersion or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PATCH /agents/{name} (admin)¶
Edit an agent's display fields (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
display_name(string or null, optional)kind(string or null, optional)description(string or null, optional)
Response 200 (application/json):
name(string, required)display_name(string or null, optional)kind(string or null, required)description(string, required)avatar_url(string or null, optional)model(string or null, optional)environment(string or null, optional)tool_count(integer, required)mcp(array of string, required)current_version(integer, required)used_by(integer, required)updated_at(string, required)current(AgentVersion or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /agents/{name}/avatar (viewer)¶
The agent's avatar image
Parameters:
name(string, in the path, required)
Response 200: no body.
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /agents/{name}/avatar (admin)¶
Set the agent's avatar image, at most 512 KiB (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (image/*):
- string
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 412, 413, 415, 422, 428.
GET /agents/{name}/documents (viewer)¶
The agent's context documents, without their content
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of DocumentSummary, required)scope(one of "agent", "team", required)owner(string, required): The agent's or the team's name.name(string, required)media_type(string, required)description(string, required)size_bytes(integer, required)sha256(string, required)created_by(string, required)created_at(string, required)updated_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
DELETE /agents/{name}/documents/{doc} (admin)¶
Remove a context document of the agent (admin)
Parameters:
name(string, in the path, required)doc(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 412, 428.
GET /agents/{name}/documents/{doc} (viewer)¶
One context document of the agent, with its content
Parameters:
name(string, in the path, required)doc(string, in the path, required)
Response 200 (application/json):
scope(one of "agent", "team", required)owner(string, required): The agent's or the team's name.name(string, required)media_type(string, required)description(string, required)size_bytes(integer, required)sha256(string, required)created_by(string, required)created_at(string, required)updated_at(string, required)content(string, required): The text of the document.
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /agents/{name}/documents/{doc} (admin)¶
Create (201) or replace (needs If-Match; 200) a context document of the agent: read-only text the worker puts under /workspace/context in the guest (admin)
Parameters:
name(string, in the path, required)doc(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
media_type(string, required)description(string, optional)content(string, required)
Response 200 (application/json):
scope(one of "agent", "team", required)owner(string, required): The agent's or the team's name.name(string, required)media_type(string, required)description(string, required)size_bytes(integer, required)sha256(string, required)created_by(string, required)created_at(string, required)updated_at(string, required)content(string, required): The text of the document.
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /agents/{name}/versions (viewer)¶
An agent's versions, newest first
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of VersionMeta, required)version(integer, required)note(string, required)created_by(string, required)created_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /agents/{name}/versions (admin)¶
Add an agent version and make it current. mcp_servers names PLATFORM MCP servers (422 for an unknown one): attaching or detaching one is a new version, and every team running the agent gets the union of its template's names and the current version's at its next run. A team's own servers are managed on /mcp-servers, not attached here (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
system_prompt(string or null, optional)tools(array of string or null, optional)confirmation_policy(one of "never_confirm", "confirm_risky" or null, optional)produces_branch(boolean or null, optional)works_on_branch(boolean or null, optional)skills(array of string or null, optional)mcp_servers(array of string or null, optional)egress(EgressInput or null, optional)model(string or null, optional)environment(string or null, optional)context(array of map of any or null, optional)note(string, optional)from_version(integer or null, optional)
Response 201 (application/json):
version(integer, required)note(string, required)created_by(string, required)created_at(string, required)system_prompt(string, required)tools(array of string, required)confirmation_policy(one of "never_confirm", "confirm_risky", required)produces_branch(boolean, required)works_on_branch(boolean or null, optional)skills(array of string, optional)mcp_servers(array of string, optional)egress(Egress, required)mode(one of "allow_all", "allowlist", required)destinations(array of string, optional)upstream_deny_cidrs(array of string, optional)context(array of any, optional)
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /agents/{name}/versions/{version} (viewer)¶
One agent version
Parameters:
name(string, in the path, required)version(integer, in the path, required)
Response 200 (application/json):
version(integer, required)note(string, required)created_by(string, required)created_at(string, required)system_prompt(string, required)tools(array of string, required)confirmation_policy(one of "never_confirm", "confirm_risky", required)produces_branch(boolean, required)works_on_branch(boolean or null, optional)skills(array of string, optional)mcp_servers(array of string, optional)egress(Egress, required)mode(one of "allow_all", "allowlist", required)destinations(array of string, optional)upstream_deny_cidrs(array of string, optional)context(array of any, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
destinations¶
GET /destinations (viewer)¶
List destinations
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Destination, required)name(string, required)host(string or null, optional)methods(array of string, required): The HTTP methods the destination allows; an empty list means any method (llm, mcp and otel are plain endpoints with no method rule).paths(array of string, required)header_allowlist(array of string, required)inject_secret(string or null, optional)git_push_ref_globs(array of string, required)what(string, required)builtin(boolean, required)github_scope(string or null, optional): api, git, repo_metadata or mcp: the credential gateway scopes the destination to the team's repository.dev_only(boolean, optional): Left out of every policy unless the controller runs with KAPELLE_EGRESS_DEV_DESTINATIONS=1.policy_name(string or null, optional)updated_at(string, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /destinations (admin)¶
Create a destination. A loopback or private host needs dev_only; inject_secret is a gateway key, mcp:
Request body (application/json):
host(string or null, optional)methods(array of string, optional)paths(array of string, optional)header_allowlist(array of string, optional)inject_secret(string or null, optional)git_push_ref_globs(array of string, optional)what(string, optional)github_scope(one of "api", "git", "repo_metadata", "mcp" or null, optional)dev_only(boolean, optional)policy_name(string or null, optional)name(string, required)
Response 201 (application/json):
name(string, required)host(string or null, optional)methods(array of string, required): The HTTP methods the destination allows; an empty list means any method (llm, mcp and otel are plain endpoints with no method rule).paths(array of string, required)header_allowlist(array of string, required)inject_secret(string or null, optional)git_push_ref_globs(array of string, required)what(string, required)builtin(boolean, required)github_scope(string or null, optional): api, git, repo_metadata or mcp: the credential gateway scopes the destination to the team's repository.dev_only(boolean, optional): Left out of every policy unless the controller runs with KAPELLE_EGRESS_DEV_DESTINATIONS=1.policy_name(string or null, optional)updated_at(string, optional)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
DELETE /destinations/{name} (admin)¶
Delete a destination no current agent version and no team role override names (not a built-in one) (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 412, 428.
GET /destinations/{name} (viewer)¶
One destination
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)host(string or null, optional)methods(array of string, required): The HTTP methods the destination allows; an empty list means any method (llm, mcp and otel are plain endpoints with no method rule).paths(array of string, required)header_allowlist(array of string, required)inject_secret(string or null, optional)git_push_ref_globs(array of string, required)what(string, required)builtin(boolean, required)github_scope(string or null, optional): api, git, repo_metadata or mcp: the credential gateway scopes the destination to the team's repository.dev_only(boolean, optional): Left out of every policy unless the controller runs with KAPELLE_EGRESS_DEV_DESTINATIONS=1.policy_name(string or null, optional)updated_at(string, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /destinations/{name} (admin)¶
Replace a destination (not a built-in one). Agents see it the next time their policy loads: when an agent starts or the gateway reloads (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
host(string or null, optional)methods(array of string, optional)paths(array of string, optional)header_allowlist(array of string, optional)inject_secret(string or null, optional)git_push_ref_globs(array of string, optional)what(string, optional)github_scope(one of "api", "git", "repo_metadata", "mcp" or null, optional)dev_only(boolean, optional)policy_name(string or null, optional)
Response 200 (application/json):
name(string, required)host(string or null, optional)methods(array of string, required): The HTTP methods the destination allows; an empty list means any method (llm, mcp and otel are plain endpoints with no method rule).paths(array of string, required)header_allowlist(array of string, required)inject_secret(string or null, optional)git_push_ref_globs(array of string, required)what(string, required)builtin(boolean, required)github_scope(string or null, optional): api, git, repo_metadata or mcp: the credential gateway scopes the destination to the team's repository.dev_only(boolean, optional): Left out of every policy unless the controller runs with KAPELLE_EGRESS_DEV_DESTINATIONS=1.policy_name(string or null, optional)updated_at(string, optional)
Errors (application/problem+json): 400, 401, 403, 404, 409, 412, 415, 422, 428.
environments¶
GET /checkpoints (viewer)¶
Checkpoint ids in use
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Checkpoint, required)id(string, required)agent(string or null, optional)from_image(string or null, optional)disk_gib(number or null, optional)created_at(string or null, optional)kind(one of "snapshot", "deep_sleep", "None", optional)used_by_environments(integer, required)used_by_roles(integer, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /environments (viewer)¶
List environments
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Environment, required)name(string, required)provider(one of "firecracker", "nevia", required)profile(string, required)vcpus(integer or null, optional)memory_mib(integer or null, optional)disk_gib(integer or null, optional)image(ImageRef or null, optional): Read-only: the image a firecracker VM of this profile boots follows the image build (GET /images), it is not set through the environment.checkpoint_id(string or null, optional)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)used_by(integer, required): Current agent versions and current template versions that name it. Editing the environment changes none of them: a template version copies the environment's values when it is created, a team copies them from its template.updated_at(string, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /environments (admin)¶
Create an environment; template versions and teams made afterwards copy it (admin)
Request body (application/json):
provider(one of "firecracker", "nevia", required)profile(string, required)checkpoint_id(string or null, optional):chk_...; required for nevia, refused otherwise.idle_after_seconds(integer, optional)deep_sleep_after_seconds(integer, optional)name(string, required)
Response 201 (application/json):
name(string, required)provider(one of "firecracker", "nevia", required)profile(string, required)vcpus(integer or null, optional)memory_mib(integer or null, optional)disk_gib(integer or null, optional)image(ImageRef or null, optional): Read-only: the image a firecracker VM of this profile boots follows the image build (GET /images), it is not set through the environment.checkpoint_id(string or null, optional)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)used_by(integer, required): Current agent versions and current template versions that name it. Editing the environment changes none of them: a template version copies the environment's values when it is created, a team copies them from its template.updated_at(string, optional)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
DELETE /environments/{name} (admin)¶
Delete an environment no current agent version or template version names (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 412, 428.
GET /environments/{name} (viewer)¶
One environment
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)provider(one of "firecracker", "nevia", required)profile(string, required)vcpus(integer or null, optional)memory_mib(integer or null, optional)disk_gib(integer or null, optional)image(ImageRef or null, optional): Read-only: the image a firecracker VM of this profile boots follows the image build (GET /images), it is not set through the environment.checkpoint_id(string or null, optional)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)used_by(integer, required): Current agent versions and current template versions that name it. Editing the environment changes none of them: a template version copies the environment's values when it is created, a team copies them from its template.updated_at(string, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /environments/{name} (admin)¶
Replace an environment's settings. Only template versions and teams made afterwards see the change; existing ones keep the values they copied (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
provider(one of "firecracker", "nevia", required)profile(string, required)checkpoint_id(string or null, optional):chk_...; required for nevia, refused otherwise.idle_after_seconds(integer, optional)deep_sleep_after_seconds(integer, optional)
Response 200 (application/json):
name(string, required)provider(one of "firecracker", "nevia", required)profile(string, required)vcpus(integer or null, optional)memory_mib(integer or null, optional)disk_gib(integer or null, optional)image(ImageRef or null, optional): Read-only: the image a firecracker VM of this profile boots follows the image build (GET /images), it is not set through the environment.checkpoint_id(string or null, optional)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)used_by(integer, required): Current agent versions and current template versions that name it. Editing the environment changes none of them: a template version copies the environment's values when it is created, a team copies them from its template.updated_at(string, optional)
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /images (viewer)¶
Agent images from the build manifests, newest first
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Image, required)name(string, required)tag(string, required)digest(string, required)size_bytes(integer or null, optional)built_at(string or null, optional)openhands_version(string or null, optional)kernel(string or null, optional)state(string or null, optional)used_by(integer, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
import¶
POST /import (admin)¶
Import roles, team templates and credgw policies from the repository (dry run by default) (admin)
Request body (application/json):
dry_run(boolean, optional)force(boolean, optional)
Response 200 (application/json):
dry_run(boolean, required)files(array of object, required)path(string, required)resource(string, required)action(one of "create", "update", "unchanged", "conflict", required)version(integer or null, optional)warnings(array of string, required)detail(string, required)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
integrations¶
GET /integrations (viewer)¶
The five integrations with their status
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Integration, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /integrations/mattermost/role-bots (viewer)¶
The Mattermost role bots: one per role of an active team, against the bots the gateway holds, with the display name and picture each shows and what differs from the agent definition. Read from the gateway live (up to 8 s when Mattermost is slow)
Response 200 (application/json):
items(array of RoleBot, required)role(string, required)state(one of "ok", "missing", "token_unusable", "unused", "unknown", required): ok: the gateway holds a bot for the role; missing: no stored token; token_unusable: a token is stored and Mattermost rejects it; unused: a bot no active team's role needs; unknown: the gateway could not say (seedetailof the list).username(string or null, required)display_name(string or null, required)expected_display_name(string, required)has_picture(boolean or null, required)expected_picture(boolean, required)problems(array of string, required)detail(string, required): Empty when the gateway answered normally; otherwise one line saying why the status is incomplete (no gateway answered, Mattermost or OpenBao not configured on it, OpenBao rejected its token).
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /integrations/{kind} (viewer)¶
One integration
Parameters:
kind(string, in the path, required)
Response 200 (application/json):
- one of:
MattermostIntegration,SlackIntegration,GithubIntegration,LinearIntegration,JiraIntegration
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /integrations/{kind} (admin)¶
Replace an integration's record: enabled and its settings. Enabling needs the kind's required settings and required secrets. A change applies when the gateway and the controller restart (admin)
Parameters:
kind(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
enabled(boolean, required)settings(map of any, optional)
Response 200 (application/json):
- one of:
MattermostIntegration,SlackIntegration,GithubIntegration,LinearIntegration,JiraIntegration
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /integrations/{kind}/deliveries (viewer)¶
Recent webhook deliveries
Parameters:
kind(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Delivery, required)id(integer, required)received_at(string, required)delivery_id(string or null, optional)event_type(string or null, optional)signature_valid(boolean, required)outcome(one of "recorded", "handled", "duplicate", "ignored_no_team", "rejected_signature", "error", required)team(string or null, optional)http_status(integer or null, optional)handling_ms(integer or null, optional)detail(string, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /integrations/{kind}/test (operator)¶
Test an integration's connection: the gateway probes Mattermost, Slack, Linear and Jira with the credentials it runs with, the controller probes GitHub; the result is stored as the integration's credentials state. No gateway answering is state unknown. At most one test per integration every 5 seconds (429 with Retry-After) (operator)
Parameters:
kind(string, in the path, required)
Response 200 (application/json):
state(one of "ok", "error", "unknown", required)detail(string, required)checked_at(string, required)
Errors (application/problem+json): 401, 403, 404, 429, 503.
mcp-servers¶
GET /mcp-servers (viewer)¶
List MCP servers: the platform ones first (scope platform, no team), then each team's
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.team(string, in the query, optional): Only this team.
Response 200 (application/json):
items(array of McpServer, required)id(string, required)name(string, required)scope(one of "platform", "team", required)team(string or null, required)transport(one of "http", "stdio", required)url(string or null, optional)destination(string or null, optional)auth(string, required)secret(SecretStatus or null, optional): The credential state from the server's destination: asecret:<ref>destination reports that reference's status (setorunset); any other kind of destination isunknown; null forauth: none.allow(array of string, required)roles(array of string, required)default(boolean, optional)state(one of "connected", "not_connected", "error", "unknown", required)status(one of "proposed", "curated", required)source(string, optional)updated_at(string, optional)warnings(array of string, optional): Only on the answer of a create or replace: roles whose egress does not include the destination, where the worker drops this server.next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /mcp-servers (operator)¶
Add an MCP server to a team. It is curated at once and reaches every role of the team (roles narrows it); attaching a PLATFORM server to an agent is a new agent version instead. The declaration is validated with the worker's own model, scanned for credentials and kept to 4096 bytes. destination must be in the destinations catalogue; its inject_secret (secret:<ref>, entered with PUT /secrets/{ref}) carries the credential, so a team that needs its own credential gets its own destination. The worker keeps the server for a role only when the destination is in that role's egress (builtins plus its egress destinations, in both modes): warnings names the roles where it is not (operator)
Request body (application/json):
transport(one of "http", "stdio", optional)url(string or null, optional)command(string or null, optional)args(array of string, optional)destination(string, required)auth(string, optional)roles(array of string, optional)tools(McpToolsInput, optional)allow(array of string, optional)deny(array of string, optional)env(map of string, optional)team(string, required)name(string, required)
Response 201 (application/json):
id(string, required)name(string, required)scope(one of "platform", "team", required)team(string or null, required)transport(one of "http", "stdio", required)url(string or null, optional)destination(string or null, optional)auth(string, required)secret(SecretStatus or null, optional): The credential state from the server's destination: asecret:<ref>destination reports that reference's status (setorunset); any other kind of destination isunknown; null forauth: none.allow(array of string, required)roles(array of string, required)default(boolean, optional)state(one of "connected", "not_connected", "error", "unknown", required)status(one of "proposed", "curated", required)source(string, optional)updated_at(string, optional)warnings(array of string, optional): Only on the answer of a create or replace: roles whose egress does not include the destination, where the worker drops this server.
Errors (application/problem+json): 400, 401, 403, 404, 409, 415, 422.
GET /mcp-servers/{id} (viewer)¶
One MCP server; the id is platform:
Parameters:
id(integer, in the path, required)
Response 200 (application/json):
id(string, required)name(string, required)scope(one of "platform", "team", required)team(string or null, required)transport(one of "http", "stdio", required)url(string or null, optional)destination(string or null, optional)auth(string, required)secret(SecretStatus or null, optional): The credential state from the server's destination: asecret:<ref>destination reports that reference's status (setorunset); any other kind of destination isunknown; null forauth: none.allow(array of string, required)roles(array of string, required)default(boolean, optional)state(one of "connected", "not_connected", "error", "unknown", required)status(one of "proposed", "curated", required)source(string, optional)updated_at(string, optional)warnings(array of string, optional): Only on the answer of a create or replace: roles whose egress does not include the destination, where the worker drops this server.
Errors (application/problem+json): 400, 401, 403, 404, 422.
DELETE /mcp-servers/{team}/{name} (operator)¶
Remove a team MCP server (operator)
Parameters:
team(string, in the path, required)name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 412, 428.
PUT /mcp-servers/{team}/{name} (operator)¶
Replace a team MCP server; a proposed one becomes curated. Same validation and warnings as the create (operator)
Parameters:
team(string, in the path, required)name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
transport(one of "http", "stdio", optional)url(string or null, optional)command(string or null, optional)args(array of string, optional)destination(string, required)auth(string, optional)roles(array of string, optional)tools(McpToolsInput, optional)allow(array of string, optional)deny(array of string, optional)env(map of string, optional)
Response 200 (application/json):
id(string, required)name(string, required)scope(one of "platform", "team", required)team(string or null, required)transport(one of "http", "stdio", required)url(string or null, optional)destination(string or null, optional)auth(string, required)secret(SecretStatus or null, optional): The credential state from the server's destination: asecret:<ref>destination reports that reference's status (setorunset); any other kind of destination isunknown; null forauth: none.allow(array of string, required)roles(array of string, required)default(boolean, optional)state(one of "connected", "not_connected", "error", "unknown", required)status(one of "proposed", "curated", required)source(string, optional)updated_at(string, optional)warnings(array of string, optional): Only on the answer of a create or replace: roles whose egress does not include the destination, where the worker drops this server.
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
POST /mcp-servers/{team}/{name}/accept (operator)¶
Promote an MCP server an agent proposed to curated; idempotent (operator)
Parameters:
team(string, in the path, required)name(string, in the path, required)
Response 200 (application/json):
id(string, required)name(string, required)scope(one of "platform", "team", required)team(string or null, required)transport(one of "http", "stdio", required)url(string or null, optional)destination(string or null, optional)auth(string, required)secret(SecretStatus or null, optional): The credential state from the server's destination: asecret:<ref>destination reports that reference's status (setorunset); any other kind of destination isunknown; null forauth: none.allow(array of string, required)roles(array of string, required)default(boolean, optional)state(one of "connected", "not_connected", "error", "unknown", required)status(one of "proposed", "curated", required)source(string, optional)updated_at(string, optional)warnings(array of string, optional): Only on the answer of a create or replace: roles whose egress does not include the destination, where the worker drops this server.
Errors (application/problem+json): 401, 403, 404.
meta¶
GET /openapi.json (viewer)¶
This document as JSON (any logged-in role; it lists routes only and holds no data)
Response 200: no body.
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /openapi.yaml (viewer)¶
This document as YAML, from the generator that writes console/api/openapi.yaml (any logged-in role)
Response 200: no body.
Errors (application/problem+json): 400, 401, 403, 404, 422.
onboarding¶
GET /onboarding (viewer)¶
The two-sentence onboarding prompt and AGENTS.md snippet for the caller's role and this deployment, with no token in them: what a person gives their own coding agent so it can manage Kapelle through this API; the docs page it points at (agents) holds the rest. Leaves an audit row onboarding.view
Response 200 (application/json):
role(one of "viewer", "operator", "admin", required)prompt(string, required): The text to paste into the person's agent, exactly two sentences: who it works for and where the API is, the docs page to read first (agents, which explains the API, the conventions, every route and the playbooks), and how to authenticate with the caller's role. The token is read from KAPELLE_API_TOKEN unless one was minted with the request.snippet(string, required): The same two sentences under a## Kapelleheading, for an AGENTS.md / CLAUDE.md file (no token unless the request asked for it).links(object, required)api(string, required)openapi_json(string, required)openapi_yaml(string, required)console(string, required)docs_console_api(string or null, optional)docs_design(string or null, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /onboarding/token (operator)¶
Mint a personal API token and get the prompt and snippet with it embedded; the token is shown once, inside prompt, try_it.curl and, when asked for, snippet. Session only: a token cannot mint a token. Audit row token.create with purpose=onboarding. The expiry is 1 to 365 days (30 by default); a token without one is refused (operator)
Request body (application/json):
name(string, optional)role(one of "viewer", "operator", "admin", optional)expires_in_days(integer, optional)include_token_in_snippet(boolean, optional)
Response 201 (application/json):
role(one of "viewer", "operator", "admin", required)prompt(string, required): The text to paste into the person's agent, exactly two sentences: who it works for and where the API is, the docs page to read first (agents, which explains the API, the conventions, every route and the playbooks), and how to authenticate with the caller's role. The token is read from KAPELLE_API_TOKEN unless one was minted with the request.snippet(string, required): The same two sentences under a## Kapelleheading, for an AGENTS.md / CLAUDE.md file (no token unless the request asked for it).links(object, required)api(string, required)openapi_json(string, required)openapi_yaml(string, required)console(string, required)docs_console_api(string or null, optional)docs_design(string or null, optional)token(Token, required): The token's record; the token itself is only insideprompt,try_it.curland, when asked for,snippet: shown once.id(integer, required)name(string, required)role(one of "viewer", "operator", "admin", required)prefix(string, required): The first 8 characters of the token body.created_at(string, required)expires_at(string, required)last_used_at(string or null, optional)revoked_at(string or null, optional)try_it(object, required)curl(string, required)expected(string, required)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
runs¶
GET /runs (viewer)¶
List runs, newest first
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.team(string, in the query, optional): Only this team.state(string, in the query, optional): Filter by derived state.source(string, in the query, optional): Only runs started from this surface.
Response 200 (application/json):
items(array of Run, required)id(string, required)work_item_id(integer, required)team(string, required)role(string or null, optional)task(string or null, optional)source(string, required)ref(string, required)state(one of "running", "waiting", "succeeded", "failed", "cancelled", required)started_at(string, required)finished_at(string or null, optional)duration_seconds(integer or null, optional)cost_usd(number or null, optional)tokens(integer or null, optional)trace_id(string or null, optional)chat_thread_url(string or null, optional)source_url(string or null, optional)jaeger_url(string or null, optional)failure_reason(string or null, optional): For a failed run: the text of the failing task, at most 1000 characters.waiting_for(null or object, optional): For a waiting run: the role that is paused, on what, and since when.delivery(null or object, optional): The branch and pull request of the run's last push.home(string, optional): The surface the run lives on: console, mattermost, slack, ...open_question(null or object, optional): For a waiting run: the newest question no reply answers. Answer it with POST /runs/{id}/replies andin_reply_to=seq.tasks(array of RunTask, optional): Only on GET /runs/{id}: every task of the run, in the order it was first seen.next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /runs/{id} (viewer)¶
One run
Parameters:
id(string, in the path, required)
Response 200 (application/json):
id(string, required)work_item_id(integer, required)team(string, required)role(string or null, optional)task(string or null, optional)source(string, required)ref(string, required)state(one of "running", "waiting", "succeeded", "failed", "cancelled", required)started_at(string, required)finished_at(string or null, optional)duration_seconds(integer or null, optional)cost_usd(number or null, optional)tokens(integer or null, optional)trace_id(string or null, optional)chat_thread_url(string or null, optional)source_url(string or null, optional)jaeger_url(string or null, optional)failure_reason(string or null, optional): For a failed run: the text of the failing task, at most 1000 characters.waiting_for(null or object, optional): For a waiting run: the role that is paused, on what, and since when.delivery(null or object, optional): The branch and pull request of the run's last push.home(string, optional): The surface the run lives on: console, mattermost, slack, ...open_question(null or object, optional): For a waiting run: the newest question no reply answers. Answer it with POST /runs/{id}/replies andin_reply_to=seq.tasks(array of RunTask, optional): Only on GET /runs/{id}: every task of the run, in the order it was first seen.task_id(string, required)role(string, required)state(string, required): working while the task is open, else its terminal A2A state (completed, failed, canceled, rejected).started_at(string, required)ended_at(string or null, optional)created_by(string or null, optional): delegation or hop; null for the run's own first task or a reply.
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /runs/{id}/cancel (operator)¶
Cancel a run; the same command as /cancel in chat. 409 for a run of a chat surface (operator)
Parameters:
id(string, in the path, required)
Response 202 (application/json):
run_id(string, required): The run's id,wi_<work_item_id>.work_item_id(integer, required)task_id(string or null, optional): Usually null: the lead's task is created after the answer. Read the run for it.state(string or null, optional)
Errors (application/problem+json): 401, 403, 404, 409, 502, 503.
GET /runs/{id}/posts (viewer)¶
The conversation of one run in order (the gateway's console posts: tasks, replies, questions, answers, notices, completion). cursor is a post's seq; 503 while the conversation store is not available
Parameters:
id(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Post, required)id(integer, required)seq(integer, required): 1, 2, 3 ... per run.kind(one of "notice", "working", "question", "answer", "completed", "failed", "reply", "command_result", "task", "artifact", "canceled", required)role(string or null, optional)author(string, required): platform, agent:, or user:console: for a Console person. text(string, required)at(string, required)task_id(string or null, optional)in_reply_to(integer or null, optional): On a reply: theseqof the question it answers.url(string or null, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /runs/{id}/replies (operator)¶
Answer a run's open question, or send a follow-up task in the same run. in_reply_to is the seq of the question (open_question.seq of the run); without it the text is a follow-up. 409 for a run of a chat surface (operator)
Parameters:
id(string, in the path, required)
Request body (application/json):
text(string, required)in_reply_to(integer or null, optional): Theseqof the question being answered; without it the text is a follow-up task in the same run.
Response 202 (application/json):
run_id(string, required): The run's id,wi_<work_item_id>.work_item_id(integer, required)task_id(string or null, optional): Usually null: the lead's task is created after the answer. Read the run for it.state(string or null, optional)
Errors (application/problem+json): 400, 401, 403, 404, 409, 415, 422, 429, 502, 503.
GET /runs/{id}/trace (viewer)¶
The spans of one run, read from Jaeger by the run's trace id (empty when Jaeger is not configured, unreachable or has no such trace)
Parameters:
id(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Span, required)start_seconds(number, required)duration_seconds(number, required)role(string, required)kind(one of "llm", "tool", "a2a", "vm", "net", required)label(string, required)cost_usd(number or null, required)span_id(string, required)parent_span_id(string or null, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /teams/{name}/runs (viewer)¶
A team's runs, newest first
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.state(string, in the query, optional): Filter by derived state.source(string, in the query, optional): Only runs started from this surface.
Response 200 (application/json):
items(array of Run, required)id(string, required)work_item_id(integer, required)team(string, required)role(string or null, optional)task(string or null, optional)source(string, required)ref(string, required)state(one of "running", "waiting", "succeeded", "failed", "cancelled", required)started_at(string, required)finished_at(string or null, optional)duration_seconds(integer or null, optional)cost_usd(number or null, optional)tokens(integer or null, optional)trace_id(string or null, optional)chat_thread_url(string or null, optional)source_url(string or null, optional)jaeger_url(string or null, optional)failure_reason(string or null, optional): For a failed run: the text of the failing task, at most 1000 characters.waiting_for(null or object, optional): For a waiting run: the role that is paused, on what, and since when.delivery(null or object, optional): The branch and pull request of the run's last push.home(string, optional): The surface the run lives on: console, mattermost, slack, ...open_question(null or object, optional): For a waiting run: the newest question no reply answers. Answer it with POST /runs/{id}/replies andin_reply_to=seq.tasks(array of RunTask, optional): Only on GET /runs/{id}: every task of the run, in the order it was first seen.next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /teams/{name}/tasks (operator)¶
Give a team a task from the Console: a new run on the console surface, the lead role gets the text. 202 with the run's id and a Location; read the answer with GET /runs/{id} and /runs/{id}/posts. 409 when the team is archived or lives on a chat surface, 429 when its budget is spent, 503 when no gateway answers (operator)
Parameters:
name(string, in the path, required)
Request body (application/json):
text(string, required)
Response 202 (application/json):
run_id(string, required): The run's id,wi_<work_item_id>.work_item_id(integer, required)task_id(string or null, optional): Usually null: the lead's task is created after the answer. Read the run for it.state(string or null, optional)
Errors (application/problem+json): 400, 401, 403, 404, 409, 415, 422, 429, 502, 503.
secrets¶
GET /secrets (viewer)¶
Secret reference metadata (never a value)
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of SecretRef, required)name(string, required)kind(string, required)status(one of "set", "unset", required)set_at(string or null, optional)rotated_at(string or null, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
DELETE /secrets/{name} (admin)¶
Remove a secret's value: a destination secret's reference is removed with it, an integration secret's stays, marked unset; a destination secret is refused with 409 while a destination's inject_secret names it (admin)
Parameters:
name(string, in the path, required)
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 503.
GET /secrets/{name} (viewer)¶
One secret reference (never a value)
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)kind(string, required)status(one of "set", "unset", required)set_at(string or null, optional)rotated_at(string or null, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /secrets/{name} (admin)¶
Write a secret's value, once; it is never returned. integrations/
Parameters:
name(string, in the path, required)
Request body (application/json):
value(string, required)
Response 204: no body.
Errors (application/problem+json): 400, 401, 403, 415, 422, 503.
session¶
GET /me (viewer)¶
Who the credentials are: the user (id, name, role) and how they were presented, session or token. The first call to make with a new API token
Response 200 (application/json):
user(SessionUser, required)id(integer, required)name(string, required)email(string or null, required)role(one of "admin", "operator", "viewer", required)auth(one of "password", "invited", required)sso_provider(string or null, optional)last_login_at(string or null, optional)invited(boolean, required)identities(array of object, required)via(one of "session", "token", required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
DELETE /session (viewer)¶
Log out
Response 204: no body.
Errors (application/problem+json): 400, 401, 403, 404, 409, 422.
GET /session (viewer)¶
The logged-in user and the CSRF token
Response 200 (application/json):
user(SessionUser, required)id(integer, required)name(string, required)email(string or null, required)role(one of "admin", "operator", "viewer", required)auth(one of "password", "invited", required)sso_provider(string or null, optional)last_login_at(string or null, optional)invited(boolean, required)identities(array of object, required)csrf_token(string, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /session (viewer)¶
Log in; sets the session cookie
Request body (application/json):
email(string, required)password(string, required)
Response 204: no body.
Errors (application/problem+json): 400, 401, 403, 415, 422, 429.
POST /session/setup (viewer)¶
Set the first password with a one-time setup token and log in (no session needed); sets the session cookie
Request body (application/json):
token(string, required)password(string, required)
Response 204: no body.
Errors (application/problem+json): 400, 401, 403, 415, 422, 429.
skills¶
GET /skills (viewer)¶
The platform skill catalogue, without bodies
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of PlatformSkillSummary, required)name(string, required)description(string, required): From the front matter of SKILL.md.size_bytes(integer, required): SKILL.md plus the text files.revision(integer, required): The catalogue revision this skill was last written at.resources(array of string, required): Paths of the text files next to SKILL.md.created_at(string, required)updated_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
DELETE /skills/{name} (admin)¶
Remove a platform skill; a file under roles/skills/ comes back at the next import (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 412, 428.
GET /skills/{name} (viewer)¶
One platform skill with the text of SKILL.md and its files
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)description(string, required)size_bytes(integer, required)revision(integer, required)resources(map of string, required): Relative path -> text of the files next to SKILL.md.created_at(string, required)updated_at(string, required)body(string, required): The text of SKILL.md.
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /skills/{name} (admin)¶
Create (201) or replace (needs If-Match; 200) a platform skill; the catalogue revision of the skill becomes the highest plus one (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
body(string, required)resources(map of string, optional)
Response 200 (application/json):
name(string, required)description(string, required)size_bytes(integer, required)revision(integer, required)resources(map of string, required): Relative path -> text of the files next to SKILL.md.created_at(string, required)updated_at(string, required)body(string, required): The text of SKILL.md.
Errors (application/problem+json): 400, 401, 403, 412, 415, 422, 428.
team-templates¶
GET /team-templates (viewer)¶
List team templates
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of TeamTemplateSummary, required)name(string, required)description(string, required)current_version(integer, required)updated_at(string, required)used_by(integer, required)lead(string or null, optional)roles(array of string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /team-templates (admin)¶
Create a team template with its first version (admin)
Request body (application/json):
lead(string, required)roles(map of TemplateRoleInput, required)sleep(SleepInput, required)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)budget_usd_per_day(number, required)isolation(one of "shared", "dedicated", optional)requires_repo(boolean, optional)rework_rounds(integer, optional)pipeline(map of string, optional)note(string, optional)name(string, required)description(string, optional)
Response 201 (application/json):
name(string, required)description(string, required)current_version(integer, required)updated_at(string, required)used_by(integer, required)current(TeamTemplateVersion or null, required)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
DELETE /team-templates/{name} (admin)¶
Delete a team template no active team uses (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 412, 428.
GET /team-templates/{name} (viewer)¶
One team template with its current version
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)description(string, required)current_version(integer, required)updated_at(string, required)used_by(integer, required)current(TeamTemplateVersion or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PATCH /team-templates/{name} (admin)¶
Edit a team template's description (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
description(string or null, optional)
Response 200 (application/json):
name(string, required)description(string, required)current_version(integer, required)updated_at(string, required)used_by(integer, required)current(TeamTemplateVersion or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /team-templates/{name}/versions (viewer)¶
A template's versions, newest first
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of VersionMeta, required)version(integer, required)note(string, required)created_by(string, required)created_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /team-templates/{name}/versions (admin)¶
Add a team template version and make it current (admin)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
lead(string, required)roles(map of TemplateRoleInput, required)sleep(SleepInput, required)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)budget_usd_per_day(number, required)isolation(one of "shared", "dedicated", optional)requires_repo(boolean, optional)rework_rounds(integer, optional)pipeline(map of string, optional)note(string, optional)
Response 201 (application/json):
version(integer, required)note(string, required)created_by(string, required)created_at(string, required)content_sha256(string or null, optional)lead(string or null, optional)roles(map of TemplateRole, required)sleep(SleepSettings, optional)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)budget_usd_per_day(number or null, optional)isolation(one of "shared", "dedicated", required)requires_repo(boolean, required)rework_rounds(integer or null, optional)pipeline(map of string, optional)
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /team-templates/{name}/versions/{version} (viewer)¶
One template version
Parameters:
name(string, in the path, required)version(integer, in the path, required)
Response 200 (application/json):
version(integer, required)note(string, required)created_by(string, required)created_at(string, required)content_sha256(string or null, optional)lead(string or null, optional)roles(map of TemplateRole, required)sleep(SleepSettings, optional)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)budget_usd_per_day(number or null, optional)isolation(one of "shared", "dedicated", required)requires_repo(boolean, required)rework_rounds(integer or null, optional)pipeline(map of string, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
teams¶
GET /spend (viewer)¶
Spend of all active teams per UTC day, with each team's series (cached 60 s)
Parameters:
days(integer, in the query, optional): How many UTC days, ending today.
Response 200 (application/json):
days(integer, required)series(array of DaySpend, required): All active teams summed per UTC day, oldest first.date(string, required): A UTC day.spend_usd(number, required)tokens(integer, required)requests(integer, required)totals(SpendTotals, required)spend_usd(number, required)tokens(integer, required)requests(integer, required)budget_usd_per_day(number, required): The sum of the active teams' daily budgets.teams(array of object, required)team(string, required)budget_usd_per_day(number, required)state(one of "normal", "warning", "exceeded", required)series(array of DaySpend, required)totals(SpendTotals, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /teams (viewer)¶
List teams
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.state(string, in the query, optional): Filter by derived state.q(string, in the query, optional): Substring of the team name.
Response 200 (application/json):
items(array of Team, required)name(string, required)template(object, required)lead(string, required)roles(array of string, required)home(object, required)repo_url(string or null, optional)links(array of Link, required)state(one of "running", "waiting", "sleeping", "idle", "archived", required): running: a work item is in progress; waiting: one waits for a person; sleeping: every placed sandbox is asleep; idle: otherwise; archived: the team was archived.status(one of "provisioning", "active", "archived", required)active_runs(integer, required)spent_usd(number or null, optional)budget_usd_per_day(number, required)budget_state(one of "normal", "warning", "exceeded", required)last_run_at(string or null, optional)created_by(string, required)created_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /teams (operator)¶
Create a team from a template; answers when the team is ready (operator)
Request body (application/json):
name(string, required)template(TemplateRef, required)name(string, required)version(integer or null, optional)repo_url(string or null, optional)home(HomeInput or null, optional)budget_usd_per_day(number or null, optional)
Response 201 (application/json):
name(string, required)template(object, required)name(string, required)version(integer or null, optional)hash(string, required)lead(string, required)roles(array of string, required)home(object, required)surface(one of "mattermost", "slack", "console", "none", required)room_id(string or null, optional)room_label(string or null, optional)repo_url(string or null, optional)links(array of Link, required)surface(string, required)external_id(string, required)label(string, required)verified(boolean, required)verification(one of "verified", "unverified", "not_applicable", required)state(one of "running", "waiting", "sleeping", "idle", "archived", required): running: a work item is in progress; waiting: one waits for a person; sleeping: every placed sandbox is asleep; idle: otherwise; archived: the team was archived.status(one of "provisioning", "active", "archived", required)active_runs(integer, required)spent_usd(number or null, optional)budget_usd_per_day(number, required)budget_state(one of "normal", "warning", "exceeded", required)last_run_at(string or null, optional)created_by(string, required)created_at(string, required)role_settings(array of object, required)role(string, required)agent(string, required)model(string, required)provider(string or null, optional)profile(string, required)checkpoint_id(string or null, optional)replicas_max(integer, required)concurrent_runs(integer, required)max_iterations(integer or null, optional)memory(RoleMemorySettings, optional)confirmation_policy(one of "never_confirm", "confirm_risky", optional)max_budget(number or null, optional)budget_duration(string or null, optional)rpm_limit(integer or null, optional)tpm_limit(integer or null, optional)pipeline_next_role(string or null, optional)skills(array of string, optional)mcp_servers(array of string, optional)members(array of object, required)user_id(integer, required)name(string, required)email(string or null, optional)role(string, required)settings(object, required)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)isolation(one of "shared", "dedicated", required)rework_rounds(integer, required)memory(MemorySettings, optional)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422, 502, 503.
DELETE /teams/{name} (operator)¶
Archive a team; answers when the archive is done (operator)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 409, 412, 428, 502, 503.
GET /teams/{name} (viewer)¶
One team with role settings and members
Parameters:
name(string, in the path, required)
Response 200 (application/json):
name(string, required)template(object, required)name(string, required)version(integer or null, optional)hash(string, required)lead(string, required)roles(array of string, required)home(object, required)surface(one of "mattermost", "slack", "console", "none", required)room_id(string or null, optional)room_label(string or null, optional)repo_url(string or null, optional)links(array of Link, required)surface(string, required)external_id(string, required)label(string, required)verified(boolean, required)verification(one of "verified", "unverified", "not_applicable", required)state(one of "running", "waiting", "sleeping", "idle", "archived", required): running: a work item is in progress; waiting: one waits for a person; sleeping: every placed sandbox is asleep; idle: otherwise; archived: the team was archived.status(one of "provisioning", "active", "archived", required)active_runs(integer, required)spent_usd(number or null, optional)budget_usd_per_day(number, required)budget_state(one of "normal", "warning", "exceeded", required)last_run_at(string or null, optional)created_by(string, required)created_at(string, required)role_settings(array of object, required)role(string, required)agent(string, required)model(string, required)provider(string or null, optional)profile(string, required)checkpoint_id(string or null, optional)replicas_max(integer, required)concurrent_runs(integer, required)max_iterations(integer or null, optional)memory(RoleMemorySettings, optional)confirmation_policy(one of "never_confirm", "confirm_risky", optional)max_budget(number or null, optional)budget_duration(string or null, optional)rpm_limit(integer or null, optional)tpm_limit(integer or null, optional)pipeline_next_role(string or null, optional)skills(array of string, optional)mcp_servers(array of string, optional)members(array of object, required)user_id(integer, required)name(string, required)email(string or null, optional)role(string, required)settings(object, required)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)isolation(one of "shared", "dedicated", required)rework_rounds(integer, required)memory(MemorySettings, optional)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PATCH /teams/{name} (operator)¶
Change a team's budget, rework rounds, sleep times, or a role's model, iteration cap, replicas, concurrent runs and confirmation policy. Not here: the LiteLLM key limits of a role (max budget, budget duration, rpm, tpm): changing them means updating the role's LiteLLM key, which nothing owns yet (deferred) (operator)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
budget_usd_per_day(number or null, optional)rework_rounds(integer or null, optional)sleep(SleepPatch or null, optional)memory(MemoryPatch or null, optional)role_settings(map of RoleSettingsPatch or null, optional)
Response 200 (application/json):
name(string, required)template(object, required)name(string, required)version(integer or null, optional)hash(string, required)lead(string, required)roles(array of string, required)home(object, required)surface(one of "mattermost", "slack", "console", "none", required)room_id(string or null, optional)room_label(string or null, optional)repo_url(string or null, optional)links(array of Link, required)surface(string, required)external_id(string, required)label(string, required)verified(boolean, required)verification(one of "verified", "unverified", "not_applicable", required)state(one of "running", "waiting", "sleeping", "idle", "archived", required): running: a work item is in progress; waiting: one waits for a person; sleeping: every placed sandbox is asleep; idle: otherwise; archived: the team was archived.status(one of "provisioning", "active", "archived", required)active_runs(integer, required)spent_usd(number or null, optional)budget_usd_per_day(number, required)budget_state(one of "normal", "warning", "exceeded", required)last_run_at(string or null, optional)created_by(string, required)created_at(string, required)role_settings(array of object, required)role(string, required)agent(string, required)model(string, required)provider(string or null, optional)profile(string, required)checkpoint_id(string or null, optional)replicas_max(integer, required)concurrent_runs(integer, required)max_iterations(integer or null, optional)memory(RoleMemorySettings, optional)confirmation_policy(one of "never_confirm", "confirm_risky", optional)max_budget(number or null, optional)budget_duration(string or null, optional)rpm_limit(integer or null, optional)tpm_limit(integer or null, optional)pipeline_next_role(string or null, optional)skills(array of string, optional)mcp_servers(array of string, optional)members(array of object, required)user_id(integer, required)name(string, required)email(string or null, optional)role(string, required)settings(object, required)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)isolation(one of "shared", "dedicated", required)rework_rounds(integer, required)memory(MemorySettings, optional)
Errors (application/problem+json): 400, 401, 403, 404, 409, 412, 415, 422, 428, 502, 503.
GET /teams/{name}/budget (viewer)¶
A team's budget and its LiteLLM spend per UTC day (cached 60 s; 503 without LiteLLM, 502 when LiteLLM fails; 404 for an unknown team and 404 with type team-archived for an archived one, whose LiteLLM team and spend are gone)
Parameters:
name(string, in the path, required)days(integer, in the query, optional): How many UTC days, ending today.
Response 200 (application/json):
team(string, required)budget_usd_per_day(number, required)state(one of "normal", "warning", "exceeded", required)spent_today_usd(number, required): LiteLLM's own spend counter of the team (resets with its budget).max_budget_usd(number or null, optional)reset_at(string or null, optional)days(integer, required)series(array of DaySpend, required): Oldest day first, today last.date(string, required): A UTC day.spend_usd(number, required)tokens(integer, required)requests(integer, required)totals(SpendTotals, required)spend_usd(number, required)tokens(integer, required)requests(integer, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /teams/{name}/documents (viewer)¶
The team's context documents, without their content
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of DocumentSummary, required)scope(one of "agent", "team", required)owner(string, required): The agent's or the team's name.name(string, required)media_type(string, required)description(string, required)size_bytes(integer, required)sha256(string, required)created_by(string, required)created_at(string, required)updated_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
DELETE /teams/{name}/documents/{doc} (operator)¶
Remove a context document of the team (operator)
Parameters:
name(string, in the path, required)doc(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 412, 428.
GET /teams/{name}/documents/{doc} (viewer)¶
One context document of the team, with its content
Parameters:
name(string, in the path, required)doc(string, in the path, required)
Response 200 (application/json):
scope(one of "agent", "team", required)owner(string, required): The agent's or the team's name.name(string, required)media_type(string, required)description(string, required)size_bytes(integer, required)sha256(string, required)created_by(string, required)created_at(string, required)updated_at(string, required)content(string, required): The text of the document.
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /teams/{name}/documents/{doc} (operator)¶
Create (201) or replace (needs If-Match; 200) a context document of the team: read-only text the worker puts under /workspace/context in the guest (operator)
Parameters:
name(string, in the path, required)doc(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
media_type(string, required)description(string, optional)content(string, required)
Response 200 (application/json):
scope(one of "agent", "team", required)owner(string, required): The agent's or the team's name.name(string, required)media_type(string, required)description(string, required)size_bytes(integer, required)sha256(string, required)created_by(string, required)created_at(string, required)updated_at(string, required)content(string, required): The text of the document.
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /teams/{name}/links (viewer)¶
A team's surface links
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Link, required)surface(string, required)external_id(string, required)label(string, required)verified(boolean, required)verification(one of "verified", "unverified", "not_applicable", required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /teams/{name}/links (operator)¶
Link a team to a repository, chat channel, Linear team or Jira project; idempotent. A GitHub link is verified (the App must be installed on the repository: 422 otherwise); the other surfaces are stored unverified. Answers the link (operator)
Parameters:
name(string, in the path, required)
Request body (application/json):
surface(one of "slack", "mattermost", "linear", "jira", "github", required)external_id(string, required)
Response 200 (application/json):
surface(string, required)external_id(string, required)label(string, required)verified(boolean, required)verification(one of "verified", "unverified", "not_applicable", required)
Errors (application/problem+json): 400, 401, 403, 404, 415, 422, 502, 503.
DELETE /teams/{name}/links/{surface}/{external_id} (operator)¶
Remove a team's link to a surface; external_id may contain slashes (owner/repo). Only the link row goes: nothing on the surface is touched (operator)
Parameters:
name(string, in the path, required)surface(string, in the path, required)external_id(string, in the path, required)
Response 204: no body.
Errors (application/problem+json): 401, 403, 404.
DELETE /teams/{name}/roles/{role}/egress (admin)¶
Remove the team's egress override for a role; the agent definition's applies (admin)
Parameters:
name(string, in the path, required)role(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Response 204: no body.
Errors (application/problem+json): 401, 403, 404, 412, 428.
GET /teams/{name}/roles/{role}/egress (viewer)¶
A team role's egress override (null when the agent definition's own applies)
Parameters:
name(string, in the path, required)role(string, in the path, required)
Response 200 (application/json):
team(string, required)role(string, required)agent(string, required)override(null or TeamRoleEgressInput, required): The team's egress for this role; null means the agent definition's own applies.
Errors (application/problem+json): 400, 401, 403, 404, 422.
PUT /teams/{name}/roles/{role}/egress (admin)¶
Set the team's egress for one role, replacing the agent definition's. On Nevia the egress mode governs traffic through the credential gateway only; the computer's own network stays open. Firecracker enforces it at the network. Allow-all allows CONNECT to any port (admin)
Parameters:
name(string, in the path, required)role(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
mode(one of "allow_all", "allowlist", required)destinations(array of string, optional)upstream_deny_cidrs(array of string, optional)
Response 200 (application/json):
team(string, required)role(string, required)agent(string, required)override(null or TeamRoleEgressInput, required): The team's egress for this role; null means the agent definition's own applies.
Errors (application/problem+json): 400, 401, 403, 404, 412, 415, 422, 428.
GET /teams/{name}/skills (viewer)¶
The team's own stored skills (curated or proposed), read-only, without bodies
Parameters:
name(string, in the path, required)limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of TeamSkillSummary, required)name(string, required)version(string, required)status(one of "curated", "proposed", required)source(string, required)updated_at(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /teams/{name}/upgrade (operator)¶
Move a team to another version of its template; role fields it set by hand are kept (operator)
Parameters:
name(string, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
version(integer, required)
Response 200 (application/json):
name(string, required)template(object, required)name(string, required)version(integer or null, optional)hash(string, required)lead(string, required)roles(array of string, required)home(object, required)surface(one of "mattermost", "slack", "console", "none", required)room_id(string or null, optional)room_label(string or null, optional)repo_url(string or null, optional)links(array of Link, required)surface(string, required)external_id(string, required)label(string, required)verified(boolean, required)verification(one of "verified", "unverified", "not_applicable", required)state(one of "running", "waiting", "sleeping", "idle", "archived", required): running: a work item is in progress; waiting: one waits for a person; sleeping: every placed sandbox is asleep; idle: otherwise; archived: the team was archived.status(one of "provisioning", "active", "archived", required)active_runs(integer, required)spent_usd(number or null, optional)budget_usd_per_day(number, required)budget_state(one of "normal", "warning", "exceeded", required)last_run_at(string or null, optional)created_by(string, required)created_at(string, required)role_settings(array of object, required)role(string, required)agent(string, required)model(string, required)provider(string or null, optional)profile(string, required)checkpoint_id(string or null, optional)replicas_max(integer, required)concurrent_runs(integer, required)max_iterations(integer or null, optional)memory(RoleMemorySettings, optional)confirmation_policy(one of "never_confirm", "confirm_risky", optional)max_budget(number or null, optional)budget_duration(string or null, optional)rpm_limit(integer or null, optional)tpm_limit(integer or null, optional)pipeline_next_role(string or null, optional)skills(array of string, optional)mcp_servers(array of string, optional)members(array of object, required)user_id(integer, required)name(string, required)email(string or null, optional)role(string, required)settings(object, required)idle_after_seconds(integer, required)deep_sleep_after_seconds(integer, required)isolation(one of "shared", "dedicated", required)rework_rounds(integer, required)memory(MemorySettings, optional)
Errors (application/problem+json): 400, 401, 403, 404, 409, 412, 415, 422, 428, 502, 503.
tokens¶
GET /tokens (viewer)¶
Your own API tokens, never their secrets (session only)
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Token, required)id(integer, required)name(string, required)role(one of "viewer", "operator", "admin", required)prefix(string, required): The first 8 characters of the token body.created_at(string, required)expires_at(string, required)last_used_at(string or null, optional)revoked_at(string or null, optional)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /tokens (viewer)¶
Create a personal API token for yourself; the token is in the answer once (session only; a token cannot mint tokens)
Request body (application/json):
name(string, required)role(one of "viewer", "operator", "admin", required)expires_in_days(integer, optional)
Response 201 (application/json):
id(integer, required)name(string, required)role(one of "viewer", "operator", "admin", required)prefix(string, required): The first 8 characters of the token body.created_at(string, required)expires_at(string, required)last_used_at(string or null, optional)revoked_at(string or null, optional)token(string, required): The token itself; shown once, never again.
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
DELETE /tokens/{id} (viewer)¶
Revoke one of your own API tokens (session only)
Parameters:
id(integer, in the path, required)
Response 204: no body.
Errors (application/problem+json): 401, 403, 404.
tools¶
GET /models (viewer)¶
The LiteLLM model aliases and the model each points at
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of ModelAlias, required)alias(string, required)model(string, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
GET /tools (viewer)¶
The built-in tool catalogue
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of Tool, required)name(string, required)group(one of "files", "shell", "delegation", "memory", "skills", "mcp", required)description(string, required)requires(array of one of "repository", "branch", required)default_on(boolean, required): Whether a new agent has the tool on.kind(one of "sdk", "native", required):sdk: runs as an OpenHands SDK tool (seemaps_to);native: one of the worker's own MCP tools, offered only when the agent names it.maps_to(string or null, required): For ansdktool, the real SDK tool it runs as (file_editororterminal: several names share one); null for a native tool.next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
users¶
GET /users (admin)¶
List Console users (admin)
Parameters:
limit(integer, in the query, optional): Page size.cursor(string, in the query, optional): Thenext_cursorof the previous page.
Response 200 (application/json):
items(array of User, required)id(integer, required)name(string, required)email(string or null, required)role(one of "admin", "operator", "viewer", required)auth(one of "password", "invited", required)sso_provider(string or null, optional)last_login_at(string or null, optional)invited(boolean, required)identities(array of object, required)mfa(boolean, required)disabled(boolean, required)next_cursor(string or null, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
POST /users (admin)¶
Create a Console account without a password; the answer carries a one-time setup token to hand to the person (there is no mail) (admin)
Request body (application/json):
email(string, required)name(string, required)role(one of "viewer", "operator", "admin", optional)
Response 201 (application/json):
id(integer, required)name(string, required)email(string or null, required)role(one of "admin", "operator", "viewer", required)auth(one of "password", "invited", required)sso_provider(string or null, optional)last_login_at(string or null, optional)invited(boolean, required)identities(array of object, required)surface(string, required)external_id(string, required)mfa(boolean, required)disabled(boolean, required)setup_token(string, required): Shown once. Hand it to the person; it sets their password.setup_expires_at(string, required)
Errors (application/problem+json): 400, 401, 403, 409, 415, 422.
GET /users/{id} (admin)¶
One user (a user may read their own) (admin)
Parameters:
id(integer, in the path, required)
Response 200 (application/json):
id(integer, required)name(string, required)email(string or null, required)role(one of "admin", "operator", "viewer", required)auth(one of "password", "invited", required)sso_provider(string or null, optional)last_login_at(string or null, optional)invited(boolean, required)identities(array of object, required)surface(string, required)external_id(string, required)mfa(boolean, required)disabled(boolean, required)
Errors (application/problem+json): 400, 401, 403, 404, 422.
PATCH /users/{id} (admin)¶
Change a user's name, role or disabled flag; never leaves the Console without an enabled admin (409); disabling revokes the user's sessions (admin)
Parameters:
id(integer, in the path, required)If-Match(string, header, required): The ETag of the resource as last read.
Request body (application/json):
name(string or null, optional)role(one of "viewer", "operator", "admin" or null, optional)disabled(boolean or null, optional)
Response 200 (application/json):
id(integer, required)name(string, required)email(string or null, required)role(one of "admin", "operator", "viewer", required)auth(one of "password", "invited", required)sso_provider(string or null, optional)last_login_at(string or null, optional)invited(boolean, required)identities(array of object, required)surface(string, required)external_id(string, required)mfa(boolean, required)disabled(boolean, required)
Errors (application/problem+json): 400, 401, 403, 404, 409, 412, 415, 422, 428.
POST /users/{id}/setup-token (admin)¶
Issue a fresh one-time setup token for a user; earlier unused ones stop working (admin)
Parameters:
id(integer, in the path, required)
Response 200 (application/json):
setup_token(string, required): Shown once. Hand it to the person; it sets their password.setup_expires_at(string, required)
Errors (application/problem+json): 401, 403, 404, 409.