Kubi API
Always-on Linux desktops for AI agents.
Kubi gives your AI agent its own Linux desktop that stays on. The agent sees the screen, clicks and types, runs commands and moves files through this API, the TypeScript SDK (@trykubi/sdk) or the hosted MCP server. A person can watch live and take over at any time.
Guides, the SDK and model integrations live at https://docs.trykubi.com. This page is the reference for every endpoint.
Quickstart
- Create an API key in the dashboard and export it:
export KUBI_API_KEY=kubi_... - Look at your screen:
GET /screens/{screen_id}/screenshotreturns the image and ascreenshot_id. - Act on what you saw:
POST /screens/{screen_id}/clickwithx,y, thescreenshot_idand"screenshot": "after". The reply carries the screen after the click.
A new screen boots in about 90 seconds. Every route starts with https://api.trykubi.com/v1.
Authentication
Send Authorization: Bearer <key> on every request. A key carries permissions: read (see screens, screenshots and history), control (click, type, scroll), exec (run commands, read and write files) and power (create, start, stop and delete screens). MCP clients sign in with OAuth at the hosted MCP server instead and get the same permissions on the screens the person picks; this API refuses those tokens. People sign in to the dashboard, not through this API: sign-in, sessions, passkeys and two-factor login are Better Auth routes under https://console.trykubi.com/api/auth, outside this spec.
Errors
Every error has one shape:
Code
Branch on code. Retry only when retryable is true. outcome: unknown means the action may have run: take a screenshot before acting again.
Retries
Send an Idempotency-Key header on every POST, clicks included. If a reply is lost, send the same request with the same key: you get the stored result, never a second click. The SDK does this for you.
Sharing a screen
There is no lock between keys: any key with the permission can call a screen at any time. Clicks and typing on one screen run one call at a time, in the order they arrive, and commands run side by side. While a person has control of a screen, calls that look at or act on it get 409 CONTROL_HELD, until they release it or 5 minutes pass with no input from them. A screenshot_id on an action refuses to act on a screen that changed since you looked, a person's input included. The optional Kubi-Run header labels calls so the screen's history groups them into runs; the SDK sends it for you.
History
Every call on a screen is saved in its history for 30 days: what was called, its parameters and typed text, which key called it, the result and the screenshot it returned. A person can turn detailed history (screenshots and typed text) off for the organization or one screen. The live view is never recorded.
Slow work
Every call answers within 30 seconds. Creating, starting and stopping a screen return an operation: poll GET /operations/{operation_id}?wait_s=25 and the call waits for a change instead of busy-looping. Long commands work the same way.
Lists
Lists take limit (1 to 100) and after (a cursor) and reply {"data": [...], "next": "<cursor or null>"}.
Rate limits
Over a limit you get 429 with Retry-After. Per key: 60 requests a minute for each screen in your organization, at least 300. Per screen: 2 screenshots and 10 other calls a second.
Versioning
/v1 only grows. Ignore fields, enum values and error codes you do not know yet. A route that will go away sends Deprecation and Sunset headers first.
Routes only a signed-in person can call from the dashboard (reading and setting a screen's proxy, setting and deleting secrets) are left out: an API key or OAuth token cannot call them.
kubi_..., read from KUBI_API_KEY by the SDK). Keys carry permissions: read (see screens, screenshots, history), control (click, type, scroll), exec (commands and files) and power (create, start, stop, delete). MCP clients sign in with OAuth at the hosted MCP server instead; this API refuses those tokens.