Skip to content

API reference

Every endpoint of Keera Gateway, generated from its OpenAPI spec. To get started, see The API. Errors are listed in Errors.

One listener serves three APIs, told apart by path:

  • /api is the inference API. It speaks the OpenAI and Anthropic formats, so their SDKs work with only a new base URL and an API key.
  • /control is the control API, which the panel and the keera command use.
  • /sandbox attaches to a sandbox.

Errors share one shape: {"error": {"message", "type", "code"}}.

Inference

OpenAI- and Anthropic-shaped requests, behind an API key.

Create a chat completion

POST /api/v1/chat/completions

OpenAI's Chat Completions API, for any chat model or router the key may use, whatever provider serves it.

See OpenAI Chat Completions.

Parameters
Parameter In Type Description
X-Request-Id header string An id for the request. Kept if it is up to 64 of A-Z a-z 0-9 . _ : -, otherwise replaced.
X-Client-Request-Id header string The client's own id, as OpenAI's API takes it: printable ASCII, up to 512 characters. It is logged and sent on to OpenAI models. An invalid one is ignored.
X-Keera-Client header string The client's own name, which wins over the User-Agent in reports.
Content-Encoding header string The body may be gzip or deflate. One of identity, gzip, x-gzip, deflate.
X-Keera-Session header string Names the session (task) this request belongs to, instead of the one the gateway derives from the opening prompt. X-Session-Id, Helicone-Session-Id and X-Claude-Code-Session-Id are read too; the first with a value wins. See docs/sessions.md.
Request body
Field Type Description
model string Required. A model or router alias from GET /api/v1/models.
messages array of object Required.
stream boolean Answer as server-sent events (text/event-stream). Default false.
max_tokens integer
max_completion_tokens integer
tools array of object
Responses
Status Description
200 The provider's answer, in this API's format. Content: application/json, text/event-stream.
400 The body or a field is not valid. Codes include invalid_body, missing_model, context_length_exceeded and unsupported_parameter.
401 No valid credential was sent.
402 A budget is spent (budget_exceeded), or the organisation's prepaid credit for the deployment's provider keys is (credit_exhausted). Not 429: it does not lift by waiting.
403 Refused by a filter (filter_refused), a model the organisation may not use until it has paid (org_limited), or a subscription key while the deployment takes none (subscriptions_off).
404 No such model, or the key may not use it (model_not_found). The message says where a model of another kind is served.
413 The body is over the gateway's limit (request_too_large, 32 MiB by default), or too long for a filter's model (filter_input_too_large).
415 A Content-Encoding other than gzip or deflate (unsupported_encoding).
429 Over a limit. Retry-After says when to try again.
502 The model's backend could not be reached or gave an answer that could not be read (upstream_unavailable, upstream_error), or a filter failed.
503 No backend serves the model (no_backend), a router or filter could not decide, or the control plane is unavailable.
default Any other error status the model's provider gave, such as 504 or 529, passed on in the caller's error shape.

Create a text completion

POST /api/v1/completions

OpenAI's legacy Completions API, for completion models.

See OpenAI Completions.

Parameters
Parameter In Type Description
X-Request-Id header string An id for the request. Kept if it is up to 64 of A-Z a-z 0-9 . _ : -, otherwise replaced.
X-Client-Request-Id header string The client's own id, as OpenAI's API takes it: printable ASCII, up to 512 characters. It is logged and sent on to OpenAI models. An invalid one is ignored.
X-Keera-Client header string The client's own name, which wins over the User-Agent in reports.
Content-Encoding header string The body may be gzip or deflate. One of identity, gzip, x-gzip, deflate.
Request body
Field Type Description
model string Required. A model or router alias from GET /api/v1/models.
prompt string or array Required.
stream boolean Answer as server-sent events (text/event-stream). Default false.
max_tokens integer
Responses
Status Description
200 The provider's answer, in this API's format. Content: application/json, text/event-stream.
400 The body or a field is not valid. Codes include invalid_body, missing_model, context_length_exceeded and unsupported_parameter.
401 No valid credential was sent.
402 A budget is spent (budget_exceeded), or the organisation's prepaid credit for the deployment's provider keys is (credit_exhausted). Not 429: it does not lift by waiting.
403 Refused by a filter (filter_refused), a model the organisation may not use until it has paid (org_limited), or a subscription key while the deployment takes none (subscriptions_off).
404 No such model, or the key may not use it (model_not_found). The message says where a model of another kind is served.
413 The body is over the gateway's limit (request_too_large, 32 MiB by default), or too long for a filter's model (filter_input_too_large).
415 A Content-Encoding other than gzip or deflate (unsupported_encoding).
429 Over a limit. Retry-After says when to try again.
502 The model's backend could not be reached or gave an answer that could not be read (upstream_unavailable, upstream_error), or a filter failed.
503 No backend serves the model (no_backend), a router or filter could not decide, or the control plane is unavailable.
default Any other error status the model's provider gave, such as 504 or 529, passed on in the caller's error shape.

Create embeddings

POST /api/v1/embeddings

For embedding models.

See OpenAI Embeddings.

Parameters
Parameter In Type Description
X-Request-Id header string An id for the request. Kept if it is up to 64 of A-Z a-z 0-9 . _ : -, otherwise replaced.
X-Client-Request-Id header string The client's own id, as OpenAI's API takes it: printable ASCII, up to 512 characters. It is logged and sent on to OpenAI models. An invalid one is ignored.
X-Keera-Client header string The client's own name, which wins over the User-Agent in reports.
Content-Encoding header string The body may be gzip or deflate. One of identity, gzip, x-gzip, deflate.
Request body
Field Type Description
model string Required. A model or router alias from GET /api/v1/models.
input string or array Required.
Responses
Status Description
200 The provider's answer, in this API's format.
400 The body or a field is not valid. Codes include invalid_body, missing_model, context_length_exceeded and unsupported_parameter.
401 No valid credential was sent.
402 A budget is spent (budget_exceeded), or the organisation's prepaid credit for the deployment's provider keys is (credit_exhausted). Not 429: it does not lift by waiting.
403 Refused by a filter (filter_refused), a model the organisation may not use until it has paid (org_limited), or a subscription key while the deployment takes none (subscriptions_off).
404 No such model, or the key may not use it (model_not_found). The message says where a model of another kind is served.
413 The body is over the gateway's limit (request_too_large, 32 MiB by default), or too long for a filter's model (filter_input_too_large).
415 A Content-Encoding other than gzip or deflate (unsupported_encoding).
429 Over a limit. Retry-After says when to try again.
502 The model's backend could not be reached or gave an answer that could not be read (upstream_unavailable, upstream_error), or a filter failed.
503 No backend serves the model (no_backend), a router or filter could not decide, or the control plane is unavailable.
default Any other error status the model's provider gave, such as 504 or 529, passed on in the caller's error shape.

Create a message

POST /api/v1/messages

Anthropic's Messages API: the same chat models, routers and guardrails as chat completions, in Anthropic's format, translated where the model is not Anthropic's. Errors come in Anthropic's shape, with type: error.

For a subscription model, Claude Code sends the caller's Claude sign-in in Authorization and the Keera key in X-Keera-Key; the gateway passes Anthropic's anthropic-ratelimit-unified-* headers back. See docs/subscriptions.md.

See Anthropic Messages.

Parameters
Parameter In Type Description
X-Request-Id header string An id for the request. Kept if it is up to 64 of A-Z a-z 0-9 . _ : -, otherwise replaced.
X-Client-Request-Id header string The client's own id, as OpenAI's API takes it: printable ASCII, up to 512 characters. It is logged and sent on to OpenAI models. An invalid one is ignored.
X-Keera-Client header string The client's own name, which wins over the User-Agent in reports.
Content-Encoding header string The body may be gzip or deflate. One of identity, gzip, x-gzip, deflate.
X-Keera-Session header string Names the session (task) this request belongs to, instead of the one the gateway derives from the opening prompt. X-Session-Id, Helicone-Session-Id and X-Claude-Code-Session-Id are read too; the first with a value wins. See docs/sessions.md.
anthropic-version header string Passed on to Anthropic models. Defaults to the version the gateway knows.
anthropic-beta header string Beta flags, passed on to Anthropic models.
beta query string Sent by Anthropic's SDKs; ignored.
Request body
Field Type Description
model string Required. A model or router alias from GET /api/v1/models.
messages array of object Required.
system string or array
max_tokens integer Required.
stream boolean Answer as server-sent events (text/event-stream). Default false.
tools array of object
Responses
Status Description
200 The provider's answer, in this API's format. Content: application/json, text/event-stream.
400 The body or a field is not valid. Codes include invalid_body, missing_model, context_length_exceeded, unsupported_parameter, and subscription_model for a subscription model on an API other than this one.
401 No valid key, or a subscription model without a Claude sign-in (missing_claude_sign_in).
402 A budget is spent (budget_exceeded), or the organisation's prepaid credit for the deployment's provider keys is (credit_exhausted). Not 429: it does not lift by waiting.
403 Refused by a filter (filter_refused), a model the organisation may not use until it has paid (org_limited), or a subscription key while the deployment takes none (subscriptions_off).
404 No such model, or the key may not use it (model_not_found). The message says where a model of another kind is served.
413 The body is over the gateway's limit (request_too_large, 32 MiB by default), or too long for a filter's model (filter_input_too_large).
415 A Content-Encoding other than gzip or deflate (unsupported_encoding).
429 Over a limit. Retry-After says when to try again.
502 The model's backend could not be reached or gave an answer that could not be read (upstream_unavailable, upstream_error), or a filter failed.
503 No backend serves the model (no_backend), a router or filter could not decide, or the control plane is unavailable.
default Any other error status the model's provider gave, such as 504 or 529, passed on in the caller's error shape.

Count a message's tokens

POST /api/v1/messages/count_tokens

Answered by the gateway from an estimate, never forwarded: forwarding would send the prompt to Anthropic without its filters. The estimate errs high and includes tools and the system prompts the guardrails and routers add. The model is checked as a real request would check it.

See Anthropic Count Message tokens.

Parameters
Parameter In Type Description
X-Request-Id header string An id for the request. Kept if it is up to 64 of A-Z a-z 0-9 . _ : -, otherwise replaced.
X-Client-Request-Id header string The client's own id, as OpenAI's API takes it: printable ASCII, up to 512 characters. It is logged and sent on to OpenAI models. An invalid one is ignored.
X-Keera-Client header string The client's own name, which wins over the User-Agent in reports.
Content-Encoding header string The body may be gzip or deflate. One of identity, gzip, x-gzip, deflate.
Request body
Field Type Description
model string Required. A model or router alias from GET /api/v1/models.
messages array of object Required.
system string or array
tools array of object
Responses
Status Description
200 The estimate.
400 The request is not valid.
401 No valid credential was sent.
403 A model the organisation may not use until it has paid (org_limited), or a subscription key while the deployment takes none.
404 No such model, or the key may not use it.
413 The body is over the gateway's limit.
415 A Content-Encoding other than gzip or deflate.
503 The control plane is unavailable.

200 response

Field Type Description
input_tokens integer

Create a response

POST /api/v1/responses

OpenAI's Responses API: the same chat models, routers and guardrails, translated where the model is not OpenAI's. On the deployment's key, store is turned off.

See OpenAI Responses.

Parameters
Parameter In Type Description
X-Request-Id header string An id for the request. Kept if it is up to 64 of A-Z a-z 0-9 . _ : -, otherwise replaced.
X-Client-Request-Id header string The client's own id, as OpenAI's API takes it: printable ASCII, up to 512 characters. It is logged and sent on to OpenAI models. An invalid one is ignored.
X-Keera-Client header string The client's own name, which wins over the User-Agent in reports.
Content-Encoding header string The body may be gzip or deflate. One of identity, gzip, x-gzip, deflate.
X-Keera-Session header string Names the session (task) this request belongs to, instead of the one the gateway derives from the opening prompt. X-Session-Id, Helicone-Session-Id and X-Claude-Code-Session-Id are read too; the first with a value wins. See docs/sessions.md.
Request body
Field Type Description
model string Required. A model or router alias from GET /api/v1/models.
input string or array Required.
instructions string
max_output_tokens integer
stream boolean Answer as server-sent events (text/event-stream). Default false.
tools array of object
Responses
Status Description
200 The provider's answer, in this API's format. Content: application/json, text/event-stream.
400 The body or a field is not valid. Codes include invalid_body, missing_model, context_length_exceeded and unsupported_parameter.
401 No valid credential was sent.
402 A budget is spent (budget_exceeded), or the organisation's prepaid credit for the deployment's provider keys is (credit_exhausted). Not 429: it does not lift by waiting.
403 Refused by a filter (filter_refused), a model the organisation may not use until it has paid (org_limited), or a subscription key while the deployment takes none (subscriptions_off).
404 No such model, or the key may not use it (model_not_found). The message says where a model of another kind is served.
413 The body is over the gateway's limit (request_too_large, 32 MiB by default), or too long for a filter's model (filter_input_too_large).
415 A Content-Encoding other than gzip or deflate (unsupported_encoding).
429 Over a limit. Retry-After says when to try again.
502 The model's backend could not be reached or gave an answer that could not be read (upstream_unavailable, upstream_error), or a filter failed.
503 No backend serves the model (no_backend), a router or filter could not decide, or the control plane is unavailable.
default Any other error status the model's provider gave, such as 504 or 529, passed on in the caller's error shape.

List models

GET /api/v1/models

The models and routers this key may use, in a list both OpenAI's and Anthropic's SDKs read. It is never paged. Locked and decision models are left out.

See OpenAI List models.

Responses
Status Description
200 OK
401 No valid credential was sent.
403 A subscription key while the deployment takes none.
503 The control plane is unavailable.

200 response

Type: InferenceModelList.

Get a model

GET /api/v1/models/{alias}

The alias may hold slashes, as in anthropic/claude-sonnet-4-5, escaped or not: the rest of the path is the alias.

See OpenAI Retrieve model.

Parameters
Parameter In Type Description
alias path string A model or router alias. It may contain /.
Responses
Status Description
200 OK
401 No valid credential was sent.
403 A subscription key while the deployment takes none.
404 No such model, or the key may not use it (model_not_found).
503 The control plane is unavailable.

200 response

Type: InferenceModel.

Warm-up probe

HEAD /api/api/hello

Some Anthropic-shaped clients send this before their first request. The path is doubled because they append /api/hello to a base URL that already ends in /api. It always answers 200.

No sign-in needed.

Responses
Status Description
200 OK

MCP

MCP servers, behind the same API keys.

Open the server's event stream

GET /api/mcp/{alias}

Passed through to the server, which may answer 405 if it has no stream.

See MCP Streamable HTTP transport.

Parameters
Parameter In Type Description
alias path string The MCP server's alias.
Mcp-Session-Id header string The MCP session, passed to the server and back.
Mcp-Protocol-Version header string Passed to the server.
Last-Event-Id header string Resumes a stream, passed to the server.
Responses
Status Description
200 The server's own messages. Content: text/event-stream.
401 No valid credential was sent.
403 The server is in a private network and the organisation has not paid yet (org_limited), or a subscription key while the deployment takes none.
404 No such server, it is disabled, or the key may not call any of its tools (mcp_server_not_found). Subscription keys reach no MCP server.
502 The server could not be reached or read (mcp_unavailable), or it refused the gateway's credential for it (mcp_credential_refused).
503 The control plane is unavailable.
default Any other status the server gave, passed on.

Send MCP messages

POST /api/mcp/{alias}

MCP's Streamable HTTP transport, behind the API key. The body is one JSON-RPC message, or a batch. The gateway lists only the tools the key may call and refuses the others, runs the key's filters over a tool call's arguments, presents the server's own credential and records each call. Everything else is passed through.

A refusal of the gateway's own is a JSON-RPC error with status 200, such as a tool the key may not call, a tool call without an id, or a tool call inside a batch. A key that may call only some tools may send only initialize, ping, tools/*, logging/setLevel and tasks/* other than tasks/list. The client sends Accept: application/json, text/event-stream, as MCP asks.

See MCP Streamable HTTP transport.

Parameters
Parameter In Type Description
alias path string The MCP server's alias.
Mcp-Session-Id header string The MCP session, passed to the server and back.
Mcp-Protocol-Version header string Passed to the server.
Mcp-Method header string Must match the message's method when sent.
Mcp-Name header string Must match the tool name or URI in the message when sent.
X-Keera-Session header string Names the session (task) this request belongs to, instead of the one the gateway derives from the opening prompt. X-Session-Id, Helicone-Session-Id and X-Claude-Code-Session-Id are read too; the first with a value wins. See docs/sessions.md.
X-Keera-Client header string The client's own name, which wins over the User-Agent.
Request body

Type: JsonRpcMessage or array of JsonRpcMessage.

Responses
Status Description
200 The server's answer, or a JSON-RPC error from the gateway. Content: application/json, text/event-stream.
202 Accepted: the body held only notifications or responses.
400 The Mcp-Method or Mcp-Name header does not match the message (JSON-RPC error -32020), or the body could not be read.
401 No valid credential was sent.
403 The server is in a private network and the organisation has not paid yet (org_limited), or a subscription key while the deployment takes none.
404 No such server, it is disabled, or the key may not call any of its tools (mcp_server_not_found). Subscription keys reach no MCP server.
502 The server could not be reached or read (mcp_unavailable), or it refused the gateway's credential for it (mcp_credential_refused).
503 The control plane is unavailable.
413 The body is over the gateway's limit.
415 A Content-Encoding other than gzip or deflate.
default Any other status the server gave, passed on. A body that is neither JSON nor an event stream is dropped.

200 response

Type: JsonRpcMessage or array of JsonRpcMessage.

End an MCP session

DELETE /api/mcp/{alias}

Passed through to the server.

See MCP Streamable HTTP transport.

Parameters
Parameter In Type Description
alias path string The MCP server's alias.
Mcp-Session-Id header string The MCP session, passed to the server and back.
Mcp-Protocol-Version header string Passed to the server.
Responses
Status Description
200 Ended.
401 No valid credential was sent.
403 The server is in a private network and the organisation has not paid yet (org_limited), or a subscription key while the deployment takes none.
404 No such server, it is disabled, or the key may not call any of its tools (mcp_server_not_found). Subscription keys reach no MCP server.
502 The server could not be reached or read (mcp_unavailable), or it refused the gateway's credential for it (mcp_credential_refused).
503 The control plane is unavailable.
default Any other status the server gave, such as 204 or 405, passed on.

Probes

Liveness, readiness and metrics, for orchestrators and scrapers.

Liveness

GET /healthz

No sign-in needed.

Responses
Status Description
200 The process is up.

200 response

Field Type Description
status ok

Readiness

GET /readyz

Ready when Postgres answers. The reason for a failure goes to the log, not the answer.

No sign-in needed.

Responses
Status Description
200 Ready.
503 The database did not answer.

200 response

Field Type Description
status ok
usage_written integer (int64) Usage events this process has written.
usage_dropped integer (int64) Usage events this process has dropped.

Prometheus metrics

GET /metrics

Metrics span every organisation, so only an operator may read them, or a scraper with the metrics token (KEERA_METRICS_TOKEN) as a bearer token. That token reads this route and nothing else.

Responses
Status Description
200 The Prometheus text exposition. Content: text/plain; version=0.0.4; charset=utf-8.
401 No valid credential was sent.
403 The caller may not do this.

Sign in

Signing in and out of the control API.

How to sign in

GET /control/auth/config

What the panel offers on its sign-in screen, before anybody has signed in.

No sign-in needed.

Responses
Status Description
200 The ways in.

200 response

Field Type Description
sso boolean Whether an identity provider is configured.
providers array of SignInProvider
passkeys boolean Whether passkeys are on.

Start a sign-in

GET /control/auth/login

Opened in a browser. It sends the browser to the identity provider, or for a password directory or passkeys to the panel's own form. keera login and Keera Chat add the cli_* parameters, so the sign-in ends at their address with a one-time code for POST /control/auth/cli/token.

A sign-in that cannot start also redirects, to /?sign_in_error=1, with the reason in the keera_sign_in_error cookie.

No sign-in needed.

Parameters
Parameter In Type Description
provider query string The identity provider's name, or passkey. It may be left out only when there is one way in.
next query string A path on this site to go to afterwards. Anything else is ignored.
cli_redirect query string (uri) Where a command-line sign-in ends: a loopback address such as http://127.0.0.1:1234/callback, or Keera Chat's callback under KEERA_CHAT_URL.
cli_challenge query string The PKCE S256 challenge, 32 bytes as unpadded base64url. Needed with cli_redirect.
cli_state query string Handed back with the code. Needed with cli_redirect; up to 128 characters.
Responses
Status Description
302 To the identity provider, to the panel's password or passkey form, or back to the sign-in screen with an error.
429 Over a limit. Retry-After says when to try again.
501 This deployment has the feature turned off. The code ends in _disabled.

Finish a sign-in

GET /control/auth/callback

Where the identity provider sends the browser back. It only works in the browser that started the sign-in.

No sign-in needed.

Parameters
Parameter In Type Description
code query string
state query string
error query string Set by the identity provider when it refused.
error_description query string
keera_login cookie string Set by GET /control/auth/login; must match state.
Responses
Status Description
302 Signed in, to next or /; for a command-line sign-in, to its address with code and state. Somebody new where sign-up is allowed goes to /?sign_up=1. A failed sign-in goes to /?sign_in_error=1, with the reason in the keera_sign_in_error cookie.
429 Over a limit. Retry-After says when to try again.

Sign in with the operator key

POST /control/auth/local

Starts a panel session that grants what the operator key grants.

No sign-in needed.

Request body
Field Type Description
key string Required.
Responses
Status Description
200 Signed in.
400 The request is not valid.
401 No valid credential was sent.
429 Over a limit. Retry-After says when to try again.
413 The body is over the size limit.

200 response

Field Type Description
ok true

Redeem a sign-in code for a CLI token

POST /control/auth/cli/token

The last step of keera login. The code works once, and only with the PKCE verifier of the process that started the sign-in. Send the token as Authorization: Bearer <token>; GET /control/v1/me says whose it is.

No sign-in needed.

Request body
Field Type Description
code string Required.
verifier string Required.
label string How the machine describes itself, for the audit log.
Responses
Status Description
200 The token.
400 The request is not valid.
401 No valid credential was sent.
429 Over a limit. Retry-After says when to try again.
413 The body is over the size limit.

200 response

Field Type Description
token string Starts with keera_cli_. Shown only here.
expires_at string (date-time)

Sign in with a directory password

POST /control/auth/password

For an LDAP directory. Attempts are also limited per account, from any address. Must be sent as application/json.

No sign-in needed.

Request body
Field Type Description
provider string Required. A provider whose password is true.
flow string The sign-in GET /control/auth/login started for keera login, from the panel's address. Leave it out to sign in to the panel.
username string Required.
password string (password) Required.
next string A path on this site to go to afterwards.
Responses
Status Description
200 Signed in.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
415 The body was not sent as application/json.
429 Over a limit. Retry-After says when to try again.
503 The directory did not answer.
413 The body is over the size limit.

200 response

Type: SignInRedirect.

Start a passkey sign-in

POST /control/auth/passkey/options

Starts a new sign-in, or with flow continues the one GET /control/auth/login started for keera login. Must be sent as application/json.

No sign-in needed.

Request body
Field Type Description
flow string
next string A path on this site to go to afterwards.
Responses
Status Description
200 What to call navigator.credentials.get with.
400 The request is not valid.
401 No valid credential was sent.
415 The body was not sent as application/json.
429 Over a limit. Retry-After says when to try again.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

200 response

Field Type Description
flow string Send it back with the answer.
options RequestOptions

Sign in with a passkey

POST /control/auth/passkey/sign-in

Each flow works once. Must be sent as application/json.

No sign-in needed.

Request body
Field Type Description
flow string Required.
credential Assertion Required.
Responses
Status Description
200 Signed in.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
415 The body was not sent as application/json.
429 Over a limit. Retry-After says when to try again.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

200 response

Type: SignInRedirect.

Start adding a passkey from a set-up link

POST /control/auth/passkey/setup/options

Nobody is signed in: the link's token is the credential. Using the options does not use up the link. Must be sent as application/json.

No sign-in needed.

Request body
Field Type Description
token string Required. The part of the set-up link after #passkey-setup=.
Responses
Status Description
200 What to call navigator.credentials.create with.
400 The request is not valid.
401 No valid credential was sent.
415 The body was not sent as application/json.
429 Over a limit. Retry-After says when to try again.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

200 response

Type: RegistrationOptions.

Add a passkey from a set-up link

POST /control/auth/passkey/setup

Stores the passkey, uses up the link and signs the person in. Must be sent as application/json.

No sign-in needed.

Request body
Field Type Description
token string Required. The same token as for the options.
challenge_id string Required.
name string Defaults to "Passkey". Cut to 64 characters.
credential Registration Required.
Responses
Status Description
200 Signed in.
400 The request is not valid.
401 No valid credential was sent.
409 It clashes with something that is already there.
415 The body was not sent as application/json.
429 Over a limit. Retry-After says when to try again.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

200 response

Type: SignInRedirect.

Read the sign-up in progress

GET /control/auth/signup

For a sign-in that matched no organisation, through a provider that allows sign-up. Says who signed in and who invited them.

No sign-in needed.

Parameters
Parameter In Type Description
keera_signup cookie string Required. Set by the callback.
Responses
Status Description
200 The sign-up.
401 No valid credential was sent.
429 Over a limit. Retry-After says when to try again.

200 response

Field Type Description
email string
invited_org_name string The organisation that added this address, if any.

Create or join an organisation

POST /control/auth/signup

Creates a limited organisation with the person as its administrator, or joins the one that invited them, and signs them in. A name another organisation has gets the person's address added.

No sign-in needed.

Parameters
Parameter In Type Description
keera_signup cookie string Required. Set by the callback.
Request body
Field Type Description
join boolean Join the inviting organisation instead of creating one.
name string The new organisation's name. Needed unless join is true.
Responses
Status Description
200 Signed in.
400 The request is not valid.
401 No valid credential was sent.
409 It clashes with something that is already there.
429 Over a limit. Retry-After says when to try again.
413 The body is over the size limit.

200 response

Type: SignInRedirect.

Sign out

POST /control/auth/logout

Ends this session or CLI token. With all=1, ends every session and CLI token of this person, in every organisation.

Parameters
Parameter In Type Description
all query string 1 to sign out everywhere. One of 1.
Responses
Status Description
200 Signed out.
401 No valid credential was sent.

200 response

Field Type Description
provider_logout_url string The identity provider's own sign-out page, or empty.

Me

The caller and their own access.

Who the caller is

GET /control/v1/me

What the panel loads first. A session gets its CSRF token here.

Responses
Status Description
200 The caller and what this deployment offers.
401 No valid credential was sent.

200 response

Field Type Description
via string One of operator_key, session, cli.
user_id string Empty for the operator key.
email string
name string
role string One of operator, admin, member.
org_id string Empty for the operator key.
unrestricted boolean Whether the caller is an operator.
can_admin_org boolean
csrf string For X-CSRF-Token. Empty unless signed in with a session.
sso boolean
providers array of SignInProvider
roles_from_directory boolean Whether a directory group decides roles, so they cannot be changed here.
gateway_url string Where clients send inference requests.
chat_url string Keera Chat, or empty.
sandboxes boolean
billing boolean
payments boolean
claude_subscriptions boolean
passkeys boolean
passkey_account boolean Whether the caller signs in with passkeys.
orgs array of Membership The organisations to switch to, by name. Only when there is more than one.
org_name string
org_limited boolean Whether the organisation is limited, as after sign-up.

Switch organisation

POST /control/v1/me/org

Moves the caller's session or CLI token to another of their organisations. Choosing one that invited them accepts the invitation. The operator key cannot switch; it names org_id on each request.

Request body
Field Type Description
org_id string Required.
Responses
Status Description
200 Switched.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
413 The body is over the size limit.

200 response

Field Type Description
org_id string
org_name string
role string One of operator, admin, member.

My keys, limits and refusals

GET /control/v1/access

Only the caller's own keys, even for an administrator, with the limits above each and the refusals of the last week.

Responses
Status Description
200 The caller's access.
401 No valid credential was sent.

200 response

Field Type Description
email string
role string One of operator, admin, member.
gateway_url string
keys array of AccessKey
refusals array of Refusal The last 20 refused or failed requests, newest first.
anonymous boolean True for the operator key, which has no keys.
operator_key boolean True when signed in to the panel with the operator key.
org object
org.id string
org.name string
used boolean Whether any of the keys has ever been used.

Tenancy

Organisations, projects, users and passkeys.

List people

GET /control/v1/users

An operator who names no organisation sees everyone, by address.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 The people.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of User

Add a person

POST /control/v1/users

Places a person in an organisation before their first sign-in. Needs an administrator of the organisation. A passkey account comes with its first set-up link; an administrator can only create one at the organisation's email domain.

Request body
Field Type Description
org_id string Only an operator, who belongs to none, needs it.
email string (email) Required.
external_id string <provider>:<subject>, such as google:1234567890. Only an operator may set it; otherwise the person is linked at their first sign-in.
role string One of admin, member. Default "member".
sign_in string Defaults to sso, or passkey on a gateway with passkeys and no identity provider. One of sso, passkey.
Responses
Status Description
201 Added.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

201 response

Everything in User, plus:

Field Type Description
passkey_link PasskeyLink Only for a passkey account.

Change a person's role

PATCH /control/v1/users/{id}

Needs an administrator of the person's organisation. Not allowed where a directory group decides roles, for an operator, or on yourself. Signs the person out, so the new role applies at once.

Parameters
Parameter In Type Description
id path string
Request body
Field Type Description
role string Required. One of admin, member.
Responses
Status Description
200 Changed.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
413 The body is over the size limit.

200 response

Field Type Description
id string
role string One of admin, member.

Turn a person off

POST /control/v1/users/{id}/disable

For someone who left. Revokes their keys, ends their sessions, removes their passkeys and stops their sandboxes. Needs an administrator of their organisation; not for yourself or an operator.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
200 Turned off.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Field Type Description
id string
email string
disabled true
revoked_keys integer
sandboxes object Only where this deployment lends out sandboxes.
sandboxes.terminated integer
sandboxes.suspended integer An engineer's sandbox is kept, suspended.
sandboxes.failed integer
warning string Set when some sandboxes could not be read or stopped.

Turn a person back on

POST /control/v1/users/{id}/enable

They can sign in again. Their old keys stay revoked. Needs an administrator of their organisation; not for yourself or an operator.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
200 Turned on.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Field Type Description
id string
email string
disabled false

Hand out a passkey set-up link

POST /control/v1/users/{id}/passkey-link

Replaces any earlier link. Turns a person who has not signed in yet into a passkey account. Needs an administrator of their organisation, and, for one, an address at the organisation's email domain.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
201 The link. It is shown only here.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
501 This deployment has the feature turned off. The code ends in _disabled.

201 response

Type: PasskeyLink.

List passkeys

GET /control/v1/passkeys

The caller's own, or with user_id someone the caller administers.

Parameters
Parameter In Type Description
user_id query string Needed with the operator key, which has no passkeys.
Responses
Status Description
200 The passkeys.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Field Type Description
data array of Passkey

Add a passkey to your account

POST /control/v1/passkeys

Finishes what POST /control/v1/passkeys/options started. Only for a passkey account, in the panel, within 15 minutes of signing in.

Request body
Field Type Description
challenge_id string Required.
name string Defaults to "Passkey". Cut to 64 characters.
credential Registration Required.
Responses
Status Description
201 Added.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
409 It clashes with something that is already there.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

201 response

Type: Passkey.

Start adding a passkey to your account

POST /control/v1/passkeys/options

For a second device. Only for a passkey account, in the panel, within 15 minutes of signing in; otherwise the code is sign_in_again or forbidden. Takes no body.

Responses
Status Description
200 What to call navigator.credentials.create with.
401 No valid credential was sent.
403 The caller may not do this.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Type: RegistrationOptions.

Remove a passkey

DELETE /control/v1/passkeys/{id}

Anyone may remove their own, but not their last. An administrator may remove anyone's in their organisation, which also signs that person out.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
200 Removed.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Field Type Description
id string
deleted true

List organisations

GET /control/v1/orgs

An operator sees every organisation. Anyone else sees only their own.

Responses
Status Description
200 The organisations.
401 No valid credential was sent.

200 response

Field Type Description
data array of Org

Create an organisation

POST /control/v1/orgs

Only an operator may. The new organisation starts with the models and sandbox classes of the template files, and a project called default.

Request body
Field Type Description
name string Required.
email_domain string Places a first sign-in from this domain in this organisation.
Responses
Status Description
201 The new organisation.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
409 The name or email domain belongs to another organisation (org_name_taken, domain_taken).
413 The body is over the size limit.

201 response

Type: Org.

Change an organisation

PATCH /control/v1/orgs/{id}

Only an operator may. Send at least one field.

Parameters
Parameter In Type Description
id path string
Request body
Field Type Description
name string
email_domain string An empty string clears it.
limited boolean false lifts the limit of an organisation that signed up.
Responses
Status Description
200 The organisation as it is now.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 The name or email domain belongs to another organisation (org_name_taken, domain_taken).
413 The body is over the size limit.

200 response

Type: Org.

Delete an organisation

DELETE /control/v1/orgs/{id}

Only an operator may, and not on the organisation they are signed in to. It removes the organisation's projects, users and keys. The request log, billing and audit log stay.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
200 What the delete removed.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It still has live sandboxes (org_has_sandboxes). Terminate them first.

200 response

Field Type Description
id string
name string
deleted true
projects integer How many projects were removed.
users integer How many users were removed.
keys integer How many keys were removed.

List projects

GET /control/v1/projects

Each project comes with its guardrails and its spend this period. An operator who sends no org_id gets every organisation's projects.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 The projects, by name.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of ProjectSummary

Create a project

POST /control/v1/projects

Only an administrator of the organisation may.

Request body
Field Type Description
org_id string The organisation. Only an operator needs it; everyone else creates in their own.
name string Required.
description string
Responses
Status Description
201 The new project.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 Another project in the organisation has that name (project_name_taken).
413 The body is over the size limit.

201 response

Type: Project.

Rename or describe a project

PATCH /control/v1/projects/{id}

Only an administrator of the organisation may. Send at least one field.

Parameters
Parameter In Type Description
id path string
Request body
Field Type Description
name string
description string An empty string clears it.
Responses
Status Description
200 The project as it is now.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 Another project in the organisation has that name (project_name_taken).
413 The body is over the size limit.

200 response

Type: Project.

Delete a project

DELETE /control/v1/projects/{id}

Only an administrator of the organisation may. Every key of the project must be revoked first. Revoked keys stay, with no project.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
200 The project is gone.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It still has keys that are not revoked (project_has_keys), or live sandboxes (project_has_sandboxes).

200 response

Field Type Description
id string
name string
deleted true
detached_keys integer How many revoked keys stayed behind, now in no project.

Keys

API keys.

List API keys

GET /control/v1/keys

Each key comes with its own guardrails, last use and spend since since.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
project_id query string Only this project's keys.
since query string How far back to count spend, as a duration such as 24h. The default is the start of this month (UTC). It is rounded down to midnight.
Responses
Status Description
200 The keys.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of KeySummary
since string (date-time) Where the counts start.

Issue an API key

POST /control/v1/keys

Only an administrator of the organisation may. The secret is in key and is shown only this once. A pending key has no secret yet: the person it is for gets one when they activate it.

Request body
Field Type Description
org_id string The organisation. Only an operator needs it.
project_id string Leave it out for the organisation's oldest project.
user_id string The person whose spend this is. Required for a subscription key and for a pending key.
name string Defaults to unnamed.
kind KeyKind
expires_in_seconds integer (int64) Lifetime from now. Not with expires_at. Neither means it never expires.
expires_at string (date-time) When it expires; must be in the future. Not with expires_in_seconds.
pending boolean Leave the key for the person named in user_id to activate.
Responses
Status Description
201 The new key.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 The person or project is not in this organisation.
409 The organisation has no projects yet (project_required).
501 A subscription key, while this deployment takes no Claude subscriptions (subscriptions_disabled).
413 The body is over the size limit.

201 response

Everything in KeyInfo, plus:

Field Type Description
key string The secret. Shown only now. Absent for a pending key.

Rename an API key

PATCH /control/v1/keys/{id}

An administrator may, and so may a member for a key in their own name.

Parameters
Parameter In Type Description
id path string
Request body
Field Type Description
name string Required.
Responses
Status Description
200 The new name.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
413 The body is over the size limit.

200 response

Field Type Description
id string
name string

Revoke an API key

DELETE /control/v1/keys/{id}

An administrator may, and so may a member for a key in their own name. The key stays listed, revoked, so its usage still has a name.

Parameters
Parameter In Type Description
id path string
Responses
Status Description
200 The key no longer works.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It was already revoked (key_revoked).

200 response

Field Type Description
id string
revoked true

Rotate an API key

POST /control/v1/keys/{id}/rotate

Issues a new key with the old one's project, person, kind and guardrails, and revokes the old one. An administrator may, and so may a member for a key in their own name, but only an administrator may set its lifetime. The secret is in key and is shown only this once.

Parameters
Parameter In Type Description
id path string
Request body
Field Type Description
name string Empty keeps the old name.
expires_in_seconds integer (int64) Lifetime from now. Not with expires_at. Neither gives the new key the old one's lifetime, counted from now.
expires_at string (date-time) When it expires; must be in the future.
Responses
Status Description
201 The new key.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It is revoked (key_revoked), not yet activated (key_pending), or belongs to a sandbox (sandbox_key).
501 A subscription key, while this deployment takes no Claude subscriptions (subscriptions_disabled).
413 The body is over the size limit.

201 response

Everything in KeyInfo, plus:

Field Type Description
key string The secret. Shown only now.
replaced_key_id string The old key, now revoked.

Activate a pending API key

POST /control/v1/keys/{id}/activate

Gives a pending key its secret. The person it is for may, and so may an administrator. The secret is in key and is shown only this once.

Parameters
Parameter In Type Description
id path string
Request body
Field Type Description
name string A new name, such as the machine's. Empty keeps it.
Responses
Status Description
200 The key, now active.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It is revoked (key_revoked), expired (key_expired) or already active (key_active).
501 A subscription key, while this deployment takes no Claude subscriptions (subscriptions_disabled).
413 The body is over the size limit.

200 response

Everything in KeyInfo, plus:

Field Type Description
key string The secret. Shown only now.

Guardrails

Limits and rules per organisation, project or key.

Read a guardrail

GET /control/v1/guardrails/{scope}/{id}

What this one scope sets on its own. A scope with no guardrail answers an empty one. Anyone in the organisation may read it.

Parameters
Parameter In Type Description
scope path ScopeType The level the guardrail is on.
id path string The id of the organisation, project or key.
Responses
Status Description
200 The scope's own guardrail.
400 The request is not valid.
401 No valid credential was sent.
404 There is no such thing, or the caller may not see it.

200 response

Type: Limits.

Set a guardrail

PUT /control/v1/guardrails/{scope}/{id}

Replaces the scope's guardrail with the body. Only an administrator of the organisation may. On an organisation, only an operator may change allowed_repos; an administrator who leaves it out keeps it.

Parameters
Parameter In Type Description
scope path ScopeType The level the guardrail is on.
id path string The id of the organisation, project or key.
Request body

Type: Limits.

Responses
Status Description
200 The guardrail as stored, cleaned up.
400 The guardrail is not valid, or names a filter or MCP server the organisation does not have (filter_not_found, mcp_server_not_found).
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 An organisation's allow-list leaves out a model that one of its filters or routers runs on (filter_model_not_allowed, router_model_not_allowed).
413 The body is over the size limit.

200 response

Type: Limits.

Read the guardrails that apply

GET /control/v1/guardrails/{scope}/{id}/effective

Every level above the scope, and what they combine to, as the gateway combines them. It answers "why is this key refused". Anyone in the organisation may read it.

Parameters
Parameter In Type Description
scope path ScopeType The level the guardrail is on.
id path string The id of the organisation, project or key.
Responses
Status Description
200 The levels and their result.
400 The request is not valid.
401 No valid credential was sent.
404 There is no such thing, or the caller may not see it.

200 response

Type: Effective.

Filters

List filters

GET /control/v1/filters

Every member may read it, instructions included. With stats=1 it also says what each filter did in the window.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
stats query string "1" adds stats, from and to. One of 1.
from query string (date-time) Start of the stats window. Defaults to 30 days ago.
to query string (date-time) End of the stats window. Defaults to now.
since query string The window as a duration back from now, such as 24h.
Responses
Status Description
200 The organisation's filters.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of Filter
stats map of FilterStat By alias. A filter that did not run is left out.
from string (date-time)
to string (date-time)

Create or replace a filter

PUT /control/v1/filters/{alias}

Administrators only. A rewrite or gate filter needs model and prompt, and no rules. A pattern filter needs rules, and no model or prompt. See docs/filters.md.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, up to 64.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
model string The model the filter runs on: a chat model, or a decision model for a gate. The organisation must allow it.
mode string Empty or left out means rewrite. One of rewrite, gate, pattern.
shadow boolean Run, record, but enforce nothing. Left out means enforce.
prompt string The instruction for the filter's model, up to 16 KiB.
rules array of FilterRule
description string
Responses
Status Description
200 The filter as saved.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
413 The body is over the size limit.

200 response

Type: Filter.

Delete a filter

DELETE /control/v1/filters/{alias}

Administrators only. A filter that a router step or a guardrail still uses is refused with 409.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, up to 64.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 Deleted.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.

200 response

Type: AliasDeleted.

Run a filter over a sample

POST /control/v1/filters/{alias}/check

Administrators only. Sends a fixed sample through the filter and shows what came back. A filter that fails still answers 200, with ok false. It runs the filter's model, so it can cost money.

Parameters
Parameter In Type Description
alias path string
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 What the check found.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Type: FilterProbe.

One filter's report

GET /control/v1/filters/{alias}/report

Every member may read it. A deleted filter keeps its report; then filter is left out.

Parameters
Parameter In Type Description
alias path string
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Defaults to 30 days ago.
to query string (date-time) Defaults to now.
since query string The window as a duration back from now, such as 24h.
Responses
Status Description
200 The report.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Field Type Description
report FilterReport
filter Filter
project_names map of string Project names by id.

Routers

List routers

GET /control/v1/routers

Every member may read it. With stats=1 it also says what went through each router in the window.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
stats query string "1" adds stats, from and to. One of 1.
from query string (date-time) Start of the stats window. Defaults to 30 days ago.
to query string (date-time) End of the stats window. Defaults to now.
since query string The window as a duration back from now, such as 24h.
Responses
Status Description
200 The organisation's routers.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of Router
stats map of RouterStat By alias. A router nobody called is left out.
from string (date-time)
to string (date-time)

Create or replace a router

PUT /control/v1/routers/{alias}

Administrators only. The alias may not be one of the organisation's models (409). A new router is put on the organisation's allow-list, if it has one, unless the list leaves out a model the router reaches; warning then says so. See docs/routers.md.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, up to 64.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
display_name string
description string
start RouterStep Required.
Responses
Status Description
200 The router as saved.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
413 The body is over the size limit.

200 response

Type: SavedRouter.

Delete a router

DELETE /control/v1/routers/{alias}

Administrators only. It also comes off the organisation's allow-list. A router that a project's or key's allow-list still names is refused with 409.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, up to 64.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 Deleted.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.

200 response

Type: AliasDeleted.

Check a router's route steps

POST /control/v1/routers/{alias}/check

Administrators only. An instruction step is given sample prompts and shows where each went; the other modes ask their destinations whether they answer. It can cost money. A router with no route step, or no step named by step, answers 400.

Parameters
Parameter In Type Description
alias path string
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
step query string Check only the route step with this id.
Responses
Status Description
200 One probe per route step checked.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Type: RouterCheck.

One router's report

GET /control/v1/routers/{alias}/report

Every member may read it. A deleted router keeps its report; then router is left out.

Parameters
Parameter In Type Description
alias path string
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Defaults to 30 days ago.
to query string (date-time) Defaults to now.
since query string The window as a duration back from now, such as 24h.
Responses
Status Description
200 The report.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Field Type Description
report RouterReport
router Router

Models

List models

GET /control/v1/models

Anyone signed in may read it. Only the organisation's administrators see backends (null for everyone else) and has_api_key. With stats=1 it adds each model's time to first token in the window.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
stats query string "1" adds stats, from and to. One of 1.
from query string (date-time) Start of the stats window. Defaults to 30 days ago.
to query string (date-time) End of the stats window. Defaults to now.
since query string The window as a duration back from now, such as 24h.
Responses
Status Description
200 The organisation's models.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of OrgModel
stats map of ModelStat By alias. A model with no answers is left out.
from string (date-time)
to string (date-time)

List hosted providers

GET /control/v1/providers

The hosted providers this build knows, with their models and prices. Anyone signed in may read it; only an administrator sees endpoint (empty for everyone else).

Responses
Status Description
200 The providers.
401 No valid credential was sent.

200 response

Field Type Description
data array of HostedProvider

Create or replace a model

PUT /control/v1/models/{alias}

Administrators only. A new model is put on the organisation's allow-list, if it has one. For a provider whose key the deployment holds, the backend and prices are set by the gateway and api_key must be left out. See docs/providers.md.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, with optional slashes between parts, such as anthropic/claude-sonnet-4-5. Send a slash as %2F.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
kind string Defaults to chat. One of chat, completion, embedding, decision.
backends array of string (uri) http or https base URLs, such as http://vllm:8000/v1. At least one, unless the deployment holds the provider's key.
backend_model string Required. The name the backend serves the model under.
provider string A hosted provider from GET /control/v1/providers, or empty for a self-served model.
display_name string
description string
input_micros_per_mtok integer (int64) Micro-units per million input tokens.
output_micros_per_mtok integer (int64) Micro-units per million output tokens.
cached_input_micros_per_mtok integer (int64) 0 means the input price.
cache_write_micros_per_mtok integer (int64) 0 means the input price.
long_prompt PriceTier
max_context_tokens integer
release_date string (date) YYYY-MM-DD.
location string Where the model runs, such as ch, usa or onprem. Left out, it comes from the provider or the backend address.
subscription boolean Paid by each caller's own Claude plan. Needs provider anthropic, kind chat, and no api_key. See docs/subscriptions.md.
enabled boolean Left out, a new model starts on and an existing one keeps its state.
api_key string The backend's credential. Left out keeps the stored one, and "" removes it. Needed again when backends moves to another host.
alias string Ignored; the path names the model.
org_id string Ignored.
has_api_key boolean Ignored.
platform_key boolean Ignored.
Responses
Status Description
200 The model as stored.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
501 This deployment has the feature turned off. The code ends in _disabled.
413 The body is over the size limit.

200 response

Type: OrgModel.

Delete a model

DELETE /control/v1/models/{alias}

Administrators only. It also comes off every allow-list in the organisation. A model a router step still uses is refused with 409.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, with optional slashes between parts, such as anthropic/claude-sonnet-4-5. Send a slash as %2F.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 Deleted.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.

200 response

Type: AliasDeleted.

Check a model works

POST /control/v1/models/{alias}/check

Administrators only. Calls the model's own backend with a tool and a question only that tool can answer. A model that fails still answers 200, with ok false. It can cost money.

Parameters
Parameter In Type Description
alias path string Send a slash as %2F.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 What the check found.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Type: ModelProbe.

Send a chat from the panel

POST /control/v1/playground/chat

Sends one OpenAI-shaped chat completion with one of the caller's own API keys, named by key_id. It goes through the same data plane as POST /api/v1/chat/completions: that key's guardrails, budgets and usage apply, and the answer is the same, streamed as server-sent events when the body asks for stream. The operator key cannot use it, nor can a subscription key.

Parameters
Parameter In Type Description
key_id query string Required. One of the caller's own API keys.
X-Keera-Client header string Only keera-chat is kept, and only from a CLI sign-in. Every other request is recorded as keera-playground.
Request body
Field Type Description
model string Required. A model or router alias the key may call.
messages array of object Required.
stream boolean
Responses
Status Description
200 The completion, as the inference API answers it. Content: application/json, text/event-stream.
400 The request is not valid.
401 No valid credential was sent.
402 The model is on the deployment's key and the organisation has no credit left.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.
429 Over a limit. Retry-After says when to try again.
502 The model's backend failed or could not be reached.
413 The body is over the size limit.

200 response

Type: object. An OpenAI chat completion.

MCP servers

List MCP servers

GET /control/v1/mcp-servers

Anyone in the organisation may read it. Only its administrators see url (empty for everyone else), auth_header and has_api_key.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 The organisation's MCP servers.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of MCPServer

Create or replace an MCP server

PUT /control/v1/mcp-servers/{alias}

Administrators only. See docs/mcp.md.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, up to 64.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
url string (uri) Required. The server's Streamable HTTP endpoint, http or https.
description string
auth_header string The header the credential goes in. Empty means Authorization, as a bearer token; any other header gets the credential as it is.
enabled boolean Left out, a new server starts on and an existing one keeps its state.
api_key string Left out keeps the stored one, and "" removes it. Needed again when url moves to another host.
Responses
Status Description
200 The server as stored.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
413 The body is over the size limit.

200 response

Type: MCPServer.

Delete an MCP server

DELETE /control/v1/mcp-servers/{alias}

Administrators only. A server a guardrail's tool allow-list still names is refused with 409.

Parameters
Parameter In Type Description
alias path string Lowercase letters, digits and inner hyphens, up to 64.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 Deleted.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 It clashes with something that is already there.

200 response

Type: AliasDeleted.

List tool calls

GET /control/v1/tool-calls

Administrators only, for one organisation. Newest first, a page at a time. With summary=1 it adds up the calls of each tool instead, most called first, with no paging.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Defaults to 30 days ago.
to query string (date-time) Defaults to now.
since query string The window as a duration back from now, such as 24h.
project_id query string
key_id query string
user_id query string
server query string An MCP server's alias.
tool query string
limit query integer Rows per page. Defaults to 100, at most 5000.
before query integer (int64) The next_before of the previous page.
summary query string "1" adds up the calls per tool. One of 1.
Responses
Status Description
200 The calls, or with summary=1 the totals per tool.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

One of:

Calls

Field Type Description
from string (date-time)
to string (date-time)
data array of ToolCallRow
next_before integer (int64) Pass as before for the next page. Left out on an empty page.

Summary

Field Type Description
from string (date-time)
to string (date-time)
data array of ToolSummary

Connect

Setting up clients against this gateway.

List the clients to connect

GET /control/v1/connect

The coding agents that can be pointed at this gateway, with their templates, and the gateway address to put in them. Anyone signed in may read it.

Responses
Status Description
200 The client catalogue.
401 No valid credential was sent.

200 response

Field Type Description
data array of ConnectClient
gateway_url string (uri) The inference API's base URL, as a browser reaches it.

Fill in every client's configuration

POST /control/v1/connect/render

Renders each client's block for the given models, the default first. No key is sent: a block that holds one carries placeholder instead.

Request body
Field Type Description
models array of ConnectModel The models and routers the key may call, the default first.
Responses
Status Description
200 One rendered block per client.
400 The request is not valid.
401 No valid credential was sent.
413 The body is over the size limit.

200 response

Field Type Description
data array of object
data[].client string The client's id.
data[].config string The filled-in block.
data[].run string What to type once it is configured.
placeholder string The text that stands in a block for the key.

List the caller's own clients

GET /control/v1/clients

Which clients called with the caller's own keys in the window, and with which key, newest first. Even an administrator sees only their own. The window is widened to whole days. The operator key has no keys, so it gets an empty list and anonymous.

Parameters
Parameter In Type Description
from query string (date-time) Defaults to 30 days ago.
to query string (date-time) Defaults to now.
since query string The window as a duration back from now, such as 24h.
Responses
Status Description
200 The clients.
400 The request is not valid.
401 No valid credential was sent.

200 response

Field Type Description
from string (date-time)
to string (date-time)
data array of ConnectedClient
anonymous boolean The caller is not a person, so has no keys.

Reports

Usage, requests, sessions, audit and diagnostics.

Check the deployment

GET /control/v1/diagnostics

What a deployment is missing, as keera doctor shows it. Nothing is written. An administrator sees their organisation's checks; the deployment's own checks are only for operators.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
probe query string 1 also calls each enabled model. Only for the organisation's administrators, and needs one organisation. One of 1.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Type: Diagnosis.

Count what the deployment has

GET /control/v1/setup

For the panel's first-run steps. Without an organisation, an operator gets counts across all of them.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
401 No valid credential was sent.
403 The caller may not do this.

200 response

Type: SetupState.

Dashboard figures

GET /control/v1/overview

The dashboard in one call. Narrowed to a project, key, person or model, it is that thing's own screen.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
project_id query string Only this project's requests.
key_id query string Only this key's requests.
user_id query string Only this person's requests. A member may only name themselves.
alias query string Only this model, as clients name it.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Field Type Description
overview Overview
project_names map of string Project names by id.
key_names map of string Key names by id. Only on a narrowed overview.

Traffic between clients and models

GET /control/v1/map

The deployment as a picture: the clients that called, every model of the organisation, and the traffic between them. A member sees only the traffic of their own keys.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
from string (date-time)
to string (date-time)
gateway_url string The inference API's address, for the middle of the drawing.
gateway UsageCell Every request in the window.
clients array of MapClient Busiest first.
models array of MapModel Every model of the organisation, busiest first.
flows array of MapFlow
last_minute array of integer (int64) Requests finished in each of the last 60 seconds, the most recent second first.

Usage report

GET /control/v1/usage

Requests, tokens and cost per group. Over more than two days it counts whole days, and from and to say so.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
group_by query string What each row is. Default model. One of client, day, key, model, org, project, user.
project_id query string Only this project's requests.
key_id query string Only this key's requests.
user_id query string Only this person's requests. A member may only name themselves.
alias query string Only this model, as clients name it.
format query string csv answers a spreadsheet instead. One of csv.
Responses
Status Description
200 The report, or with format=csv a spreadsheet. Content: application/json, text/csv.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Everything in ReportLabels, plus:

Field Type Description
from string (date-time)
to string (date-time)
data array of UsageBucket
chart UsageChart
client_names map of string Client labels by client key. Only with group_by=client.

Audit log

GET /control/v1/audit

Who changed what, newest first. Administrators only. Without from, to or since it has no time window.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
actor query string Only entries by this actor.
action query string Only this action, such as billing.topup.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
limit query integer Entries per page. Default 100, at most 5000.
before query integer (int64) The next_before of the previous page.
format query string csv answers a spreadsheet of every matching entry instead, up to 5000. One of csv.
Responses
Status Description
200 The report, or with format=csv a spreadsheet. Content: application/json, text/csv.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of AuditEntry
filters AuditFacets First page only.
next_before integer (int64) Pass as before for the next page. Absent on an empty page.

Request log

GET /control/v1/requests

One row per inference request, whatever happened to it. An administrator reads the whole organisation's; a member only the requests made with their own keys.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
project_id query string Only this project's requests.
key_id query string Only this key's requests.
user_id query string Only this person's requests. A member may only name themselves.
alias query string Only this model, as clients name it.
outcome query string Only requests with this outcome. unhappy is everything that did not deliver. One of ok, unhappy, failed, refused, interrupted.
status query string One exact status, such as 402, or a class, such as 5xx. Anything else filters nothing.
request_id query string One request, by the id the gateway answered with. It ignores the window.
sort query string The order. Default recent, newest first. One of recent, cost, tokens, ttft.
limit query integer Rows per page. Default 100, at most 5000.
before query integer (int64) The next_before of the previous page. Paging works only in the default order.
format query string csv answers a spreadsheet of the whole filtered window instead, up to 5000 rows. One of csv.
facets query string 1 adds filters, the values this window can be narrowed by. First page only. One of 1.
Responses
Status Description
200 The report, or with format=csv a spreadsheet. Content: application/json, text/csv.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Everything in ReportLabels, plus:

Field Type Description
from string (date-time)
to string (date-time)
data array of RequestLogEntry
outcomes RequestOutcomes Counts over the whole window. First page only.
filters RequestFacets With facets=1, first page only.
next_before integer (int64) Pass as before for the next page. Only in the default order, and absent on an empty page.

Request log, live

GET /control/v1/requests/stream

The rows GET /control/v1/requests returns, narrowed the same way, as they are written. The response stays open.

Each batch is an event named requests whose data is a JSON object:

  • data: the new rows, newest first, at most 200 (RequestLogEntry).
  • outcomes: the counts over the window since gives (RequestOutcomes).
  • next_after: the newest id sent. Pass it as after to resume.
  • project_names, key_names, user_names, user_full_names: only when a row names something the earlier labels did not.

An idle stream sends a : keepalive comment every 25 seconds. It ends when the server shuts down; reconnect with after.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
project_id query string Only this project's requests.
key_id query string Only this key's requests.
user_id query string Only this person's requests. A member may only name themselves.
alias query string Only this model, as clients name it.
outcome query string Only requests with this outcome. unhappy is everything that did not deliver. One of ok, unhappy, failed, refused, interrupted.
status query string One exact status, such as 402, or a class, such as 5xx. Anything else filters nothing.
request_id query string One request, by the id the gateway answered with. It ignores the window.
after query integer (int64) The newest id the reader already has. Default: the end of the log.
Responses
Status Description
200 A stream of server-sent events. Content: text/event-stream.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

Sessions

GET /control/v1/sessions

The request log grouped into tasks. A member sees only the sessions of their own keys. See docs/sessions.md.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
from query string (date-time) Start of the window. Default: 30 days before to.
to query string (date-time) End of the window. Default: now.
since query string The window as a duration back from now, such as 24h. from wins over it.
project_id query string Only this project's requests.
key_id query string Only this key's requests.
user_id query string Only this person's requests. A member may only name themselves.
alias query string Only this model, as clients name it.
session_key query string Only this conversation.
outcome query string Only sessions holding a request with this outcome. ok keeps those where every request was served. One of ok, unhappy, failed, refused, interrupted.
status query string How the session ended: the last request's status, such as 402, or a class, such as 5xx.
unhappy query string 1 is outcome=unhappy, for older keera commands. One of 1.
sort query string The order. Default recent, newest first. One of recent, cost, tokens, requests, duration.
limit query integer Rows per page. Default 100, at most 5000.
before query integer (int64) The next_before of the previous page. Paging works only in the default order.
format query string csv answers a spreadsheet of the whole filtered window instead, up to 5000 sessions. One of csv.
facets query string 1 adds filters, the values this window can be narrowed by. First page only. One of 1.
Responses
Status Description
200 The report, or with format=csv a spreadsheet. Content: application/json, text/csv.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Everything in ReportLabels, plus:

Field Type Description
from string (date-time)
to string (date-time)
data array of AgentSession
gap_seconds integer (int64) The idle gap that cut these sessions.
totals AgentSessionTotals Over the whole window. First page only.
filters RequestFacets With facets=1, first page only.
next_before integer (int64) Pass as before for the next page. Only in the default order, and absent on an empty page.

One session

GET /control/v1/sessions/{id}

One task from start to end. A member sees it only if every request in it was made with their own keys.

Parameters
Parameter In Type Description
id path integer (int64) The id of any request in the session.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Everything in ReportLabels, plus:

Field Type Description
session AgentSession
requests array of RequestLogEntry Every request, in order.
tool_calls array of ToolCallRow The MCP tool calls in between, oldest first.
gap_seconds integer (int64) The idle gap that cut this session.

Billing

Monthly bill

GET /control/v1/billing

One month of use of the deployment's own provider keys, per organisation and provider model, at list price. Administrators only. See docs/billing.md.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
month query string The month, in UTC. Default: the current one.
format query string csv answers a spreadsheet instead. One of csv.
Responses
Status Description
200 The report, or with format=csv a spreadsheet. Content: application/json, text/csv.
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
month string
from string (date-time)
to string (date-time)
data array of BillingRow
total BillingTotal
payments boolean Whether this deployment takes payments in advance.

Credit account

GET /control/v1/billing/account

The organisation's credit, its settings and its latest payments. Administrators only. It first looks up payments started in the last hour, so one just paid shows at once. On a deployment that takes no payments, it answers only {"payments": false}.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
payments boolean Whether this deployment takes payments. Every other field is there only when it does.
vat_bp integer (int64) VAT added to every payment, in basis points.
fee_fixed_micros integer (int64) The fixed part of the transaction fee.
fee_bp integer (int64) The share of the credit added to the fee, in basis points.
fee_min_micros integer (int64) The least the share adds to the fee.
topup_min_micros integer (int64) The smallest payment.
topup_max_micros integer (int64) The largest payment.
account CreditAccount
recent array of Payment The latest 20 payments, newest first.

Change the automatic top-up

PATCH /control/v1/billing/account

Sets the automatic top-up, and for operators whether the organisation is billed by invoice. Administrators only. Changing the top-up clears why the last one failed.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
topup_below_micros integer (int64) Below this balance the saved card is charged. Send with topup_micros; both 0 turn the top-up off.
topup_micros integer (int64) What one top-up pays, from CHF 1 in whole centimes, and at least topup_below_micros.
invoiced boolean Bill by invoice, so no credit is needed. Operators only.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
413 The body is too large.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Type: CreditAccount.

Remove the saved card

DELETE /control/v1/billing/account/card

Here and at PostFinance. The automatic top-up stops with it. Administrators only.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Field Type Description
org_id string
deleted true

Pay for credit

POST /control/v1/billing/topups

Starts a card payment and answers the payment page to send the person to. The credit is added once PostFinance says it is paid. VAT and a transaction fee come on top. Administrators only.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
amount_micros integer (int64) Required. The credit to buy, from CHF 1 to CHF 10,000, in whole centimes.
save_card boolean Save the card for automatic top-ups.
Responses
Status Description
201 Started
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
413 The body is too large.
501 This deployment has the feature turned off. The code ends in _disabled.
502 PostFinance Checkout could not start the payment. Nothing was charged. The code is payment_unavailable.

201 response

Field Type Description
payment Payment The pending payment. Its created_at is not filled in.
url string (uri) PostFinance Checkout's payment page.

List payments

GET /control/v1/billing/payments

Newest first. Administrators only; an operator without org_id sees every organisation's.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
limit query integer Payments per page. Default 50, at most 500.
before query string The next_before of the previous page: a payment id.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of Payment
next_before string Pass as before for the next page. Absent on an empty page.

Receipt of a payment

GET /control/v1/billing/payments/{id}/receipt

The invoice PostFinance renders for a paid payment. Administrators only. A grant has none.

Parameters
Parameter In Type Description
id path string The payment id.
Responses
Status Description
200 The receipt. Content: application/pdf.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 The payment has no receipt: it is a grant, or not paid. The code is no_receipt.
501 This deployment has the feature turned off. The code ends in _disabled.
502 PostFinance Checkout did not hand out the receipt. The code is receipt_unavailable.

Grant credit

POST /control/v1/billing/grants

Adds credit without a payment, or takes some away: a trial, a refund, money that came by bank transfer. Operators only.

Request body
Field Type Description
org_id string Required. The organisation to credit.
amount_micros integer (int64) Required. The credit to add, or below 0 to take away. Not 0, and at most CHF 10,000 either way.
note string Required. Why. It shows in the organisation's payments.
Responses
Status Description
201 Granted
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
413 The body is too large.
501 This deployment has the feature turned off. The code ends in _disabled.

201 response

Type: Payment.

PostFinance webhook

POST /control/billing/postfinance

PostFinance Checkout says a transaction changed. The call proves nothing: the gateway only reads the transaction back over PostFinance's own API. Anything that is not one of its transactions is acknowledged and ignored. Throttled per address.

No sign-in needed.

Request body
Field Type Description
entityId integer (int64) The transaction id.
listenerEntityTechnicalName string
spaceId integer (int64) The PostFinance space.
Responses
Status Description
200 Handled or ignored. No body.
400 The body is not JSON.
404 This deployment takes no payments. Content: text/plain.
429 Over a limit. Retry-After says when to try again.
503 The transaction could not be read. PostFinance tries again later.
413 The body is over the size limit.

Sandboxes

List sandbox classes

GET /control/v1/sandbox-classes

Open to anyone in the organisation. Also gives the driver, when there is one, and the sandbox limits of the organisation's first project, so a client can grey out what creating a sandbox would refuse.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of SandboxClass
driver object Absent when the deployment runs no sandbox driver.
driver.name string
driver.isolation SandboxIsolation The strongest tier the driver can deliver.
driver.tiers array or null Every tier the driver can deliver.
limits ResolvedSandbox

Create or replace a sandbox class

PUT /control/v1/sandbox-classes/{name}

Administrators only. The body has the shape the list returns, so a class read can be written back; org_id, created_at and updated_at are ignored and the name comes from the path. Zero or empty fields take their defaults (isolation isolated). An administrator may only use the images of the starting classes, those KEERA_SANDBOX_IMAGES allows, or the image the class already has.

Parameters
Parameter In Type Description
name path string The class name: lowercase letters, digits and hyphens.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
org_id string
name string
description string
image string Required.
isolation SandboxIsolation
cpu_millis integer
memory_mib integer
disk_mib integer
default_ttl_seconds integer
max_ttl_seconds integer
warm integer
purposes array of SandboxPurpose
created_at string (date-time)
updated_at string (date-time)
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 Not an administrator, an image the caller may not use, or a warm pool for an organisation that has not paid yet (org_limited).
413 The body is too large.

200 response

Type: SandboxClass.

Delete a sandbox class

DELETE /control/v1/sandbox-classes/{name}

Administrators only. A class in use can be deleted: running sandboxes keep their own copy of it. The answer says how many are still running.

Parameters
Parameter In Type Description
name path string The class name: lowercase letters, digits and hyphens.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.

200 response

Field Type Description
name string
deleted true
live_sandboxes integer

List sandboxes

GET /control/v1/sandboxes

A member sees only their own. An administrator sees the whole organisation's. Finished sandboxes are left out unless all=1.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
project_id query string Only this project's.
class query string Only this class.
purpose query SandboxPurpose Only engineer or agent sandboxes.
user_id query string Only this person's. Administrators only; a member always gets their own.
all query string Include finished sandboxes. Only 1 counts. One of 1.
Responses
Status Description
200 OK
401 No valid credential was sent.
403 Another organisation, or an account that is not a person.

200 response

Field Type Description
data array of Sandbox

Create a sandbox

POST /control/v1/sandboxes

The sandbox gets an API key of its own, in the project it lands in. A member cannot choose the project: it is the organisation's oldest. An organisation that signed itself up waits for its first payment. The answer comes before the machine is ready; its state is pending.

Request body
Field Type Description
org_id string Only an operator needs it.
project_id string Administrators only.
name string Required. Lowercase letters, digits and interior hyphens. It becomes a hostname.
class string Required.
purpose SandboxPurpose Defaults to engineer.
ttl_seconds integer The lifetime asked for. Left out, the class default. Above a ceiling, it is cut down, not refused.
repo string What to check out. An agent sandbox needs one.
branch string
task string The instruction an agent sandbox starts on. It is never stored.
authorized_keys array of string The ssh public keys that may open a shell. An engineer's sandbox needs at least one.
env map of string Extra environment. It cannot name a KEERA_ variable.
Responses
Status Description
201 Created
400 The request is not valid.
401 No valid credential was sent.
403 A member chose a project, or the organisation has not paid yet (org_limited).
404 There is no such thing, or the caller may not see it.
409 Refused: no such class, a name already in use, or over a quota (sandbox_state).
413 The body is too large.
501 This deployment has the feature turned off. The code ends in _disabled.

201 response

Type: Sandbox.

Get a sandbox

GET /control/v1/sandboxes/{ref}

Parameters
Parameter In Type Description
ref path string The sandbox's id (sbx_...) or its name. A name is unique only for one person in one organisation, so an operator naming one also passes org_id.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 More than one person has a live sandbox of that name (sandbox_ambiguous).

200 response

Type: Sandbox.

Terminate a sandbox

DELETE /control/v1/sandboxes/{ref}

The machine goes; the row stays, so its cost still resolves. An administrator may terminate any sandbox in their organisation.

Parameters
Parameter In Type Description
ref path string The sandbox's id (sbx_...) or its name. A name is unique only for one person in one organisation, so an operator naming one also passes org_id.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 The sandbox's state does not allow it, or the manager refused. The code is sandbox_state, sandbox_not_ready or sandbox_ambiguous.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Field Type Description
id string
name string
terminated true

Suspend a sandbox

POST /control/v1/sandboxes/{ref}/suspend

Releases the compute and keeps the volume. It finishes in the background, so the answer is the state read back now, not the state asked for.

Parameters
Parameter In Type Description
ref path string The sandbox's id (sbx_...) or its name. A name is unique only for one person in one organisation, so an operator naming one also passes org_id.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 The sandbox's state does not allow it, or the manager refused. The code is sandbox_state, sandbox_not_ready or sandbox_ambiguous.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Type: Sandbox.

Resume a sandbox

POST /control/v1/sandboxes/{ref}/resume

Brings back a suspended or expired sandbox. An expired one gets a new key and lifetime, under the guardrail as it is now. It finishes in the background, so the answer is the state read back now.

Parameters
Parameter In Type Description
ref path string The sandbox's id (sbx_...) or its name. A name is unique only for one person in one organisation, so an operator naming one also passes org_id.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 The sandbox's state does not allow it, or the manager refused. The code is sandbox_state, sandbox_not_ready or sandbox_ambiguous.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Type: Sandbox.

Extend a sandbox's lifetime

POST /control/v1/sandboxes/{ref}/extend

Sets the expiry to now plus the lifetime asked for, cut down to the class's and the guardrail's ceilings. No body is the same as {}: the class default.

Parameters
Parameter In Type Description
ref path string The sandbox's id (sbx_...) or its name. A name is unique only for one person in one organisation, so an operator naming one also passes org_id.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Request body
Field Type Description
ttl_seconds integer
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.
404 There is no such thing, or the caller may not see it.
409 The sandbox has expired (resume it instead) or is no longer live (sandbox_state).
413 The body is too large.
501 This deployment has the feature turned off. The code ends in _disabled.

200 response

Field Type Description
id string
name string
expires_at string (date-time)

Report sandbox usage

GET /control/v1/sandbox-usage

Administrators only. Sandbox time grouped by class, project or person. A sandbox counts whole in the window it was created in. The window is the last 30 days unless from, to or since say otherwise.

Parameters
Parameter In Type Description
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
group_by query string How to group. Defaults to class. One of class, project, user. Default "class".
from query string (date-time) Start of the window, RFC 3339.
to query string (date-time) End of the window, RFC 3339. Defaults to now.
since query string A duration back from now, such as 24h. from wins over it.
Responses
Status Description
200 OK
400 The request is not valid.
401 No valid credential was sent.
403 The caller may not do this.

200 response

Field Type Description
data array of SandboxUsage
group_by string One of class, project, user.
from string (date-time)
to string (date-time)

Open a byte stream to a sandbox port

GET /sandbox/v1/{ref}/tcp/{port}

An HTTP/1.1 upgrade, not JSON. The gateway authenticates the caller as the control API does, dials the port inside the sandbox, answers 101 and copies bytes both ways until one end stops. A connection idle for two hours is closed. Port 2222 is the sandbox's ssh server; keera sandbox ssh and keera sandbox proxy are the clients.

Only the sandbox's owner or an operator may attach. An administrator can see and terminate a colleague's sandbox but not open it.

Parameters
Parameter In Type Description
ref path string The sandbox's id (sbx_...) or its name. A name is unique only for one person in one organisation, so an operator naming one also passes org_id.
port path integer An unprivileged port inside the sandbox. 2222 is ssh.
org_id query string The organisation to act on. Only an operator, who belongs to none, needs it; everyone else acts on their own.
Upgrade header keera-sandbox/1 Required. The protocol token. It is versioned, because the keera command may be older than the gateway.
Connection header Upgrade Required.
Responses
Status Description
101 Switching protocols. From here on the connection carries the port's raw bytes.
400 The port is not between 1024 and 65535, or an operator named a sandbox without an organisation.
401 No valid credential was sent.
403 The sandbox is somebody else's.
404 There is no such thing, or the caller may not see it.
409 The sandbox's state does not allow it, or the manager refused. The code is sandbox_state, sandbox_not_ready or sandbox_ambiguous.
426 The Upgrade header is missing or names another protocol (upgrade_required). A proxy in front may have stripped it.
501 This deployment has the feature turned off. The code ends in _disabled.

Get a fresh repository credential

POST /sandbox/v1/git-credential

Asked for by a sandbox, not a person: git-credential-keera calls it when the token it holds has five minutes left. It authenticates with the sandbox's own API key (KEERA_API_KEY) as a bearer token, so it stops working when the sandbox ends or its owner is disabled. The replaced token is revoked where the forge allows it. There is no request body.

The answer is in the format git's credential helpers speak. It takes 10 requests a minute per client address on each replica.

Responses
Status Description
200 The credential, as key=value lines. password_expiry_utc is a Unix time, and only there when the token runs out. Content: text/plain.
401 No key, or a key that belongs to no live sandbox.
404 There is no such thing, or the caller may not see it.
409 Nothing to refresh: the sandbox has no repository, is not running or has run out of time (sandbox_state), or the forge gave no token (no_git_credential).
429 Over a limit. Retry-After says when to try again.
501 This deployment has the feature turned off. The code ends in _disabled.

Spec

This description, as YAML or JSON.

This spec, as YAML

GET /openapi.yaml

No sign-in needed.

Responses
Status Description
200 The spec, with info.version set to the running build. Content: application/yaml.

This spec, as JSON

GET /openapi.json

No sign-in needed.

Responses
Status Description
200 The spec, with info.version set to the running build.