{
 "openapi": "3.1.2",
 "info": {
  "title": "Kubi API",
  "version": "2026-10-22",
  "summary": "Always-on Linux desktops for AI agents.",
  "description": "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.\n\nGuides, the SDK and model integrations live at https://docs.trykubi.com. This page is the reference for every endpoint.\n\n## Quickstart\n\n1. Create an API key in the dashboard and export it: `export KUBI_API_KEY=kubi_...`\n2. Look at your screen: `GET /screens/{screen_id}/screenshot` returns the image and a `screenshot_id`.\n3. Act on what you saw: `POST /screens/{screen_id}/click` with `x`, `y`, the `screenshot_id` and `\"screenshot\": \"after\"`. The reply carries the screen after the click.\n\nA new screen boots in about 90 seconds. Every route starts with `https://api.trykubi.com/v1`.\n\n## Authentication\n\nSend `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.\n\n## Errors\n\nEvery error has one shape:\n\n```json\n{\"error\": {\"code\": \"STALE_OBSERVATION\", \"message\": \"Input ran since your screenshot. Take a new screenshot first.\", \"request_id\": \"req_...\", \"retryable\": false, \"outcome\": \"not_applied\", \"docs_url\": \"https://docs.trykubi.com/errors#stale_observation\"}}\n```\n\nBranch on `code`. Retry only when `retryable` is true. `outcome: unknown` means the action may have run: take a screenshot before acting again.\n\n## Retries\n\nSend 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.\n\n## Sharing a screen\n\nThere 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.\n\n## History\n\nEvery 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.\n\n## Slow work\n\nEvery 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.\n\n## Lists\n\nLists take `limit` (1 to 100) and `after` (a cursor) and reply `{\"data\": [...], \"next\": \"<cursor or null>\"}`.\n\n## Rate limits\n\nOver 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.\n\n## Versioning\n\n`/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.\n\n\nRoutes 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.",
  "contact": {
   "name": "Kubi support",
   "email": "support@trykubi.com"
  }
 },
 "servers": [
  {
   "url": "https://api.trykubi.com/v1",
   "description": "Production",
   "x-internal": false
  }
 ],
 "security": [
  {
   "bearerAuth": []
  }
 ],
 "tags": [
  {
   "name": "Screens",
   "description": "Create, list, rename, power and delete screens."
  },
  {
   "name": "Operations",
   "description": "Work that takes longer than one call."
  },
  {
   "name": "Templates",
   "description": "Saved screens that new screens start from, in seconds."
  },
  {
   "name": "Snapshots",
   "description": "Saved states of a screen's disk, and reset to one of them or to how the screen started."
  },
  {
   "name": "Screenshots",
   "description": "See the screen: JSON with a base64 image, raw image bytes, or a zoomed region."
  },
  {
   "name": "Actions",
   "description": "Click, type, press keys, scroll, drag and move the pointer. One action per call, or a batch of up to 20 in one round trip."
  },
  {
   "name": "Commands",
   "description": "Run shell commands. Short ones answer inline, long ones return an id to poll."
  },
  {
   "name": "Files",
   "description": "Copy files to and from a screen, list folders."
  },
  {
   "name": "Clipboard",
   "description": "Read and set the screen's clipboard text."
  },
  {
   "name": "Live view",
   "description": "Watch a screen in a browser, or show it to a person."
  },
  {
   "name": "History",
   "description": "Every call on a screen with the screenshot it returned, kept 30 days."
  },
  {
   "name": "Account",
   "description": "Who is calling, limits, and the spec itself."
  },
  {
   "name": "Network",
   "description": "Send a screen's traffic through your own proxy."
  },
  {
   "name": "Secrets",
   "description": "Values kept in each screen's memory and put into every command's environment, never on its disk. The API never shows them."
  }
 ],
 "paths": {
  "/screens": {
   "get": {
    "operationId": "listScreens",
    "summary": "List screens",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nScreens in your organization that this credential may see, newest first.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/Limit"
     },
     {
      "$ref": "#/components/parameters/After"
     },
     {
      "name": "label",
      "in": "query",
      "required": false,
      "description": "Filter by `key:value`.",
      "schema": {
       "type": "string",
       "maxLength": 141,
       "pattern": "^[a-z0-9_.-]{1,40}:.{0,100}$"
      }
     },
     {
      "name": "state",
      "in": "query",
      "required": false,
      "description": "Filter by power state.",
      "schema": {
       "type": "string",
       "maxLength": 20,
       "enum": [
        "provisioning",
        "starting",
        "running",
        "stopping",
        "stopped",
        "restarting",
        "deleting",
        "failed"
       ]
      }
     },
     {
      "name": "kind",
      "in": "query",
      "required": false,
      "description": "Only persistent or only disposable screens.",
      "schema": {
       "$ref": "#/components/schemas/ScreenKind"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ScreenList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "name": "arthur-main",
             "kind": "persistent",
             "ends_at": null,
             "delete_after": null,
             "template_id": null,
             "spend": null,
             "created_by": {
              "type": "person",
              "credential_id": null,
              "credential_name": null,
              "user_id": "usr_01jb5k2m8q3w9x4p6n7a"
             },
             "color": "green",
             "labels": {
              "project": "invoices"
             },
             "state": "running",
             "state_reason": null,
             "desktop": {
              "width": 1920,
              "height": 1080
             },
             "screenshot_default": {
              "width": 1280,
              "height": 720
             },
             "image": "debian-13-desktop",
             "size": "standard",
             "resources": {
              "vcpu": 4,
              "memory_mb": 8192,
              "disk_gb": 50
             },
             "region": "eu-1",
             "health": {
              "display": "ok",
              "input": "ok",
              "exec": "ok",
              "viewer": "ok",
              "observed_at": "2026-10-22T09:14:07Z"
             },
             "control": {
              "held": false,
              "since": null,
              "releases_at": null
             },
             "viewers": 1,
             "incarnation": 3,
             "operation_id": null,
             "last_action_at": "2026-10-22T09:13:58Z",
             "created_at": "2026-10-20T16:02:11Z",
             "updated_at": "2026-10-22T09:14:07Z"
            }
           ],
           "next": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { data } = await client.screens.list({ limit: 20 });"
     }
    ]
   },
   "post": {
    "operationId": "createScreen",
    "summary": "Create a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nCreates a screen and starts it, blank or from `template_id`. Returns 202 with an operation; the screen is listed at once as `provisioning`. A persistent screen (the default) goes on the monthly bill: the prorated price for the rest of the month is invoiced first and the machine is made once it is paid; a card that declines or needs 3-D Secure fails the operation with PAYMENT_REQUIRED and a pay link; while the organization is on hold for an unpaid invoice, the call itself answers 402 PAYMENT_REQUIRED. A disposable screen is paid from usage credit, its first billing step at once; with no credit: 402 CREDIT_EXHAUSTED. `ends_in_s` gives a disposable screen an end time. A blank screen resumes a desktop the host booted ahead and is ready in seconds; one from a template boots its disk. Over the organization's screen cap (both kinds count): 402 QUOTA_EXCEEDED. No free machine: 409 NO_CAPACITY, nothing charged.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ScreenCreate"
       },
       "examples": {
        "named": {
         "value": {
          "name": "arthur-main",
          "labels": {
           "project": "invoices"
          }
         }
        },
        "disposable": {
         "value": {
          "name": "bench-run-14",
          "kind": "disposable",
          "template_id": "tpl_01jb9a2m5k8q3w9x4z6t",
          "ends_in_s": 14400
         }
        }
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Accepted. Poll the operation (or getScreen).",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.create",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": "booting",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: QUOTA_EXCEEDED, PAYMENT_REQUIRED, CREDIT_EXHAUSTED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED, ORGANIZATION_SUSPENDED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: NO_CAPACITY, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\": \"arthur-main\", \"labels\": {\"project\": \"invoices\"}}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screens.create({ name: \"arthur-main\" });\nawait client.operations.wait(op.id);"
     }
    ]
   }
  },
  "/screens/{screen_id}": {
   "get": {
    "operationId": "getScreen",
    "summary": "Get a screen",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nState, health per capability and resources. Cheap: call it as often as you like within the rate limit.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Screen"
        },
        "examples": {
         "running": {
          "value": {
           "id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "name": "arthur-main",
           "kind": "persistent",
           "ends_at": null,
           "delete_after": null,
           "template_id": null,
           "spend": null,
           "created_by": {
            "type": "person",
            "credential_id": null,
            "credential_name": null,
            "user_id": "usr_01jb5k2m8q3w9x4p6n7a"
           },
           "color": "green",
           "labels": {
            "project": "invoices"
           },
           "state": "running",
           "state_reason": null,
           "desktop": {
            "width": 1920,
            "height": 1080
           },
           "screenshot_default": {
            "width": 1280,
            "height": 720
           },
           "image": "debian-13-desktop",
           "size": "standard",
           "resources": {
            "vcpu": 4,
            "memory_mb": 8192,
            "disk_gb": 50
           },
           "region": "eu-1",
           "health": {
            "display": "ok",
            "input": "ok",
            "exec": "ok",
            "viewer": "ok",
            "observed_at": "2026-10-22T09:14:07Z"
           },
           "control": {
            "held": false,
            "since": null,
            "releases_at": null
           },
           "viewers": 1,
           "incarnation": 3,
           "operation_id": null,
           "last_action_at": "2026-10-22T09:13:58Z",
           "created_at": "2026-10-20T16:02:11Z",
           "updated_at": "2026-10-22T09:14:07Z"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst screen = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").get();"
     }
    ]
   },
   "patch": {
    "operationId": "updateScreen",
    "summary": "Rename, recolour or relabel a screen",
    "description": "Needs the `power` permission. Answers within 2 s.\n\nChanges name, colour, labels or a disposable screen's end time. Nothing restarts.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ScreenUpdate"
       },
       "examples": {
        "rename": {
         "value": {
          "name": "billing-bot",
          "color": "blue",
          "labels": {
           "project": "invoices",
           "owner": "nika"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Screen"
        },
        "examples": {
         "renamed": {
          "value": {
           "id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "name": "billing-bot",
           "kind": "persistent",
           "ends_at": null,
           "delete_after": null,
           "template_id": null,
           "spend": null,
           "created_by": {
            "type": "person",
            "credential_id": null,
            "credential_name": null,
            "user_id": "usr_01jb5k2m8q3w9x4p6n7a"
           },
           "color": "blue",
           "labels": {
            "project": "invoices"
           },
           "state": "running",
           "state_reason": null,
           "desktop": {
            "width": 1920,
            "height": 1080
           },
           "screenshot_default": {
            "width": 1280,
            "height": 720
           },
           "image": "debian-13-desktop",
           "size": "standard",
           "resources": {
            "vcpu": 4,
            "memory_mb": 8192,
            "disk_gb": 50
           },
           "region": "eu-1",
           "health": {
            "display": "ok",
            "input": "ok",
            "exec": "ok",
            "viewer": "ok",
            "observed_at": "2026-10-22T09:14:07Z"
           },
           "control": {
            "held": false,
            "since": null,
            "releases_at": null
           },
           "viewers": 1,
           "incarnation": 3,
           "operation_id": null,
           "last_action_at": "2026-10-22T09:13:58Z",
           "created_at": "2026-10-20T16:02:11Z",
           "updated_at": "2026-10-22T09:14:07Z"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X PATCH 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\": \"billing-bot\", \"color\": \"blue\", \"labels\": {\"project\": \"invoices\", \"owner\": \"nika\"}}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").update({ name: \"billing-bot\" });"
     }
    ]
   },
   "delete": {
    "operationId": "deleteScreen",
    "summary": "Delete a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nStops the screen and deletes it. A persistent screen's disk is kept 7 days so a person can undo it in the dashboard, then destroyed; that soft delete is the guard. It leaves the bill at once, and the unused days are credited to the next invoice. A disposable screen's delete is final: its disk is destroyed at once and its metering stops. API keys need `power`; MCP clients can never delete. A repeat while it runs returns the same operation.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "deleting": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.delete",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": "stopping",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, OPERATION_IN_PROGRESS."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X DELETE 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").delete();"
     }
    ]
   }
  },
  "/screens/{screen_id}/start": {
   "post": {
    "operationId": "startScreen",
    "summary": "Start a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nStart a stopped screen. Its disk is as you left it. Returns 202 and an operation. Already in the target state: 202 with an operation that succeeds at once.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.start",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": "booting",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: PAYMENT_REQUIRED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, IDEMPOTENCY_KEY_IN_USE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/start' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").start();"
     }
    ]
   }
  },
  "/screens/{screen_id}/stop": {
   "post": {
    "operationId": "stopScreen",
    "summary": "Stop a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nShut the screen down gracefully. The disk is kept, so it stays on the bill. Returns 202 and an operation. Already in the target state: 202 with an operation that succeeds at once.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.stop",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": "shutting down",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: PAYMENT_REQUIRED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, CONTROL_HELD, IDEMPOTENCY_KEY_IN_USE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/stop' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").stop();"
     }
    ]
   }
  },
  "/screens/{screen_id}/restart": {
   "post": {
    "operationId": "restartScreen",
    "summary": "Restart a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nReboot the screen. The incarnation goes up, so old screenshots and exec ids stop applying. Returns 202 and an operation. Already in the target state: 202 with an operation that succeeds at once.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.restart",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": "rebooting",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: PAYMENT_REQUIRED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, CONTROL_HELD, IDEMPOTENCY_KEY_IN_USE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/restart' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").restart();"
     }
    ]
   }
  },
  "/screens/{screen_id}/convert": {
   "post": {
    "operationId": "convertScreen",
    "summary": "Switch a screen's kind",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nMakes a disposable screen persistent or a persistent one disposable, with no restart. To persistent: it joins the monthly subscription with the prorated price invoiced first (PAYMENT_REQUIRED if the card fails), its end time goes and metering stops. To disposable: it leaves the subscription (the unused days are credited to the next invoice) and draws on credit from now (CREDIT_EXHAUSTED with none). Already that kind: 202 with an operation that succeeds at once.",
    "tags": [
     "Screens"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ScreenConvert"
       },
       "examples": {
        "keep": {
         "value": {
          "kind": "persistent"
         }
        }
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.convert",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": "switching billing",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: PAYMENT_REQUIRED, CREDIT_EXHAUSTED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/convert' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"kind\": \"persistent\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").convert({ kind: \"persistent\" });"
     }
    ]
   }
  },
  "/templates": {
   "get": {
    "operationId": "listTemplates",
    "summary": "List templates",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nThe organization's templates, newest first.",
    "tags": [
     "Templates"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/Limit"
     },
     {
      "$ref": "#/components/parameters/After"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/TemplateList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "id": "tpl_01jb9a2m5k8q3w9x4z6t",
             "name": "osworld-base",
             "state": "ready",
             "source_screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "resources": {
              "vcpu": 4,
              "memory_mb": 8192,
              "disk_gb": 50
             },
             "with_memory": false,
             "size_bytes": 9663676416,
             "screens_started": 41,
             "last_used_at": "2026-10-22T09:14:07Z",
             "created_at": "2026-10-20T11:02:00Z"
            }
           ],
           "next": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/templates' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { data } = await client.templates.list();"
     }
    ]
   },
   "post": {
    "operationId": "createTemplate",
    "summary": "Save a screen as a template",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nSaves the screen's disk; `with_memory` says whether screens started from it also come back with the open windows it was saved with. A running screen pauses for a few seconds while it is saved. Returns 202 with an operation; the template is listed at once as `saving`. Secrets are never on the disk, so none is saved. Over the template cap: 402 QUOTA_EXCEEDED.",
    "tags": [
     "Templates"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TemplateCreate"
       },
       "examples": {
        "save": {
         "value": {
          "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
          "name": "osworld-base"
         }
        }
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "template.create",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": "tpl_01jb9a2m5k8q3w9x4z6t",
           "snapshot_id": null,
           "error": null,
           "progress": "saving the disk",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: QUOTA_EXCEEDED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, CONTROL_HELD, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/templates' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"screen_id\": \"scr_7f2kq9w3x8m4b6tzp1ac\", \"name\": \"osworld-base\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.templates.create({ screen_id: \"scr_7f2kq9w3x8m4b6tzp1ac\", name: \"osworld-base\" });"
     }
    ]
   }
  },
  "/templates/{template_id}": {
   "get": {
    "operationId": "getTemplate",
    "summary": "Get a template",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nOne template.",
    "tags": [
     "Templates"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/TemplateId"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Template"
        },
        "examples": {
         "ready": {
          "value": {
           "id": "tpl_01jb9a2m5k8q3w9x4z6t",
           "name": "osworld-base",
           "state": "ready",
           "source_screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "resources": {
            "vcpu": 4,
            "memory_mb": 8192,
            "disk_gb": 50
           },
           "with_memory": false,
           "size_bytes": 9663676416,
           "screens_started": 41,
           "last_used_at": "2026-10-22T09:14:07Z",
           "created_at": "2026-10-20T11:02:00Z"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/templates/tpl_01jb9a2m5k8q3w9x4z6t' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst tpl = await client.templates.get(\"tpl_01jb9a2m5k8q3w9x4z6t\");"
     }
    ]
   },
   "delete": {
    "operationId": "deleteTemplate",
    "summary": "Delete a template",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nDeletes it at once, with no undo. Screens started from it keep running.",
    "tags": [
     "Templates"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/TemplateId"
     }
    ],
    "responses": {
     "204": {
      "description": "Deleted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X DELETE 'https://api.trykubi.com/v1/templates/tpl_01jb9a2m5k8q3w9x4z6t' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.templates.delete(\"tpl_01jb9a2m5k8q3w9x4z6t\");"
     }
    ]
   }
  },
  "/screens/{screen_id}/snapshots": {
   "get": {
    "operationId": "listSnapshots",
    "summary": "List snapshots",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nThe screen's snapshots, newest first; at most 3.",
    "tags": [
     "Snapshots"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/SnapshotList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "id": "snap_01jb8a2m5k8q3w9x4z6t",
             "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "name": "before-upgrade",
             "state": "ready",
             "size_bytes": 1288490188,
             "created_at": "2026-10-22T09:14:07Z"
            }
           ],
           "next": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/snapshots' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { data } = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").snapshots.list();"
     }
    ]
   },
   "post": {
    "operationId": "createSnapshot",
    "summary": "Snapshot a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nSaves the screen's disk as it is now, as a point to reset to. A running screen keeps running: its writes are flushed first, and the snapshot holds what a sudden power cut would leave, never the screen's memory or its secrets. Returns 202 with an operation; the snapshot is listed at once as `creating`. Up to 3 per screen, included in the price; over that, or once the screen's snapshots hold 50 GB of changed data: 402 QUOTA_EXCEEDED, delete one first. Snapshots stay on the screen's host: backups and exports carry only the current disk, so a snapshot is an undo point, not a backup. They go with the screen when it is deleted.",
    "tags": [
     "Snapshots"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SnapshotCreate"
       },
       "examples": {
        "named": {
         "value": {
          "name": "before-upgrade"
         }
        }
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.snapshot",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": "snap_01jb8a2m5k8q3w9x4z6t",
           "error": null,
           "progress": "saving the disk",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: QUOTA_EXCEEDED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/snapshots' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\": \"before-upgrade\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").snapshots.create({ name: \"before-upgrade\" });\nawait client.operations.wait(op.id);"
     }
    ]
   }
  },
  "/screens/{screen_id}/snapshots/{snapshot_id}": {
   "delete": {
    "operationId": "deleteSnapshot",
    "summary": "Delete a snapshot",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nDeletes it at once, with no undo, and frees the space it held. The screen is not touched.",
    "tags": [
     "Snapshots"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/SnapshotId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "204": {
      "description": "Deleted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X DELETE 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/snapshots/snap_01jb8a2m5k8q3w9x4z6t' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").snapshots.delete(\"snap_01jb8a2m5k8q3w9x4z6t\");"
     }
    ]
   }
  },
  "/screens/{screen_id}/reset": {
   "post": {
    "operationId": "resetScreen",
    "summary": "Reset a screen",
    "description": "Needs the `power` permission. Answers within 5 s.\n\nPuts the screen's disk back to a snapshot or, with `snapshot_id` null, to how the screen started (the clean image, or its template's disk), then starts it. Everything written since is lost, and so are the snapshots taken after the one gone back to (all of them for a reset to the start), since the disk rolls back along one line. The id, name, kind, bill, proxy and secrets stay; the incarnation goes up, so old screenshots and exec ids stop applying. Returns 202 with an operation. API keys need `power`; MCP clients can never reset.",
    "tags": [
     "Snapshots"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ResetRequest"
       },
       "examples": {
        "snapshot": {
         "value": {
          "snapshot_id": "snap_01jb8a2m5k8q3w9x4z6t"
         }
        },
        "start": {
         "value": {
          "snapshot_id": null
         }
        }
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Accepted.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "accepted": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.reset",
           "status": "running",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": "snap_01jb8a2m5k8q3w9x4z6t",
           "error": null,
           "progress": "rolling back the disk",
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired",
      "description": "Payment needed: PAYMENT_REQUIRED, CREDIT_EXHAUSTED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: OPERATION_IN_PROGRESS, CONTROL_HELD, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/reset' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"snapshot_id\": \"snap_01jb8a2m5k8q3w9x4z6t\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").reset({ snapshot_id: \"snap_01jb8a2m5k8q3w9x4z6t\" });\nawait client.operations.wait(op.id);"
     }
    ]
   }
  },
  "/operations/{operation_id}": {
   "get": {
    "operationId": "getOperation",
    "summary": "Get an operation",
    "description": "Needs the `read` permission. Answers within 28 s.\n\nStatus of create, start, stop, restart or delete. Pass `wait_s` to hold the reply until it changes. Long polls are served from in-memory events, never by polling the database or the host.",
    "tags": [
     "Operations"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/OperationId"
     },
     {
      "$ref": "#/components/parameters/WaitS"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Operation"
        },
        "examples": {
         "done": {
          "value": {
           "id": "op_01jb7q0x9v2k8m4n6p3r",
           "kind": "screen.create",
           "status": "succeeded",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "template_id": null,
           "snapshot_id": null,
           "error": null,
           "progress": null,
           "created_at": "2026-10-22T09:14:07Z",
           "updated_at": "2026-10-22T09:14:07Z",
           "done_at": "2026-10-22T09:14:07Z"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/operations/op_01jb7q0x9v2k8m4n6p3r' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst op = await client.operations.get(\"op_01jb7q0x9v2k8m4n6p3r\", { wait_s: 25 });"
     }
    ]
   }
  },
  "/screens/{screen_id}/operations": {
   "get": {
    "operationId": "listOperations",
    "summary": "List a screen's operations",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nThe screen's operations (create, start, stop, restart, delete, restore, convert, snapshot, reset and template saves), newest first, finished ones included. Kept 30 days.",
    "tags": [
     "Operations"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Limit"
     },
     {
      "$ref": "#/components/parameters/After"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OperationList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "id": "op_01jb7q0x9v2k8m4n6p3r",
             "kind": "screen.create",
             "status": "succeeded",
             "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "template_id": null,
             "snapshot_id": null,
             "error": null,
             "progress": null,
             "created_at": "2026-10-22T09:14:07Z",
             "updated_at": "2026-10-22T09:14:07Z",
             "done_at": "2026-10-22T09:14:07Z"
            },
            {
             "id": "op_01jb6z3k8q2w9x4m7n5p",
             "kind": "screen.stop",
             "status": "succeeded",
             "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "template_id": null,
             "snapshot_id": null,
             "error": null,
             "progress": null,
             "created_at": "2026-10-21T18:02:40Z",
             "updated_at": "2026-10-21T18:02:49Z",
             "done_at": "2026-10-21T18:02:49Z"
            }
           ],
           "next": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/operations' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { data } = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").operations.list({ limit: 20 });"
     }
    ]
   }
  },
  "/screens/{screen_id}/screenshot": {
   "get": {
    "operationId": "getScreenshot",
    "summary": "Take a screenshot",
    "description": "Needs the `read` permission. Answers within 5 s.\n\nCaptures the desktop now, scaled to 1280x720 by default, and returns JSON: base64 image plus its id, size and input_seq. Add `region` to zoom. For raw bytes (curl, `<img>`) use getScreenshotImage. A copy is saved in the screen's history unless detailed history is off; the reply itself is `Cache-Control: no-store`.",
    "tags": [
     "Screenshots"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "name": "size",
      "in": "query",
      "required": false,
      "description": "Image size, WIDTHxHEIGHT or `full`.",
      "schema": {
       "$ref": "#/components/schemas/ShotSize"
      }
     },
     {
      "name": "format",
      "in": "query",
      "required": false,
      "description": "png (default), webp or jpeg.",
      "schema": {
       "$ref": "#/components/schemas/ImageFormat"
      }
     },
     {
      "name": "quality",
      "in": "query",
      "required": false,
      "description": "webp and jpeg quality, 1 to 100.",
      "schema": {
       "type": "integer",
       "format": "int32",
       "minimum": 1,
       "maximum": 100,
       "default": 80
      }
     },
     {
      "name": "max_bytes",
      "in": "query",
      "required": false,
      "description": "Cap on the encoded image: quality steps down, then the image shrinks.",
      "schema": {
       "$ref": "#/components/schemas/MaxBytes"
      }
     },
     {
      "name": "region",
      "in": "query",
      "required": false,
      "description": "Zoom: `x0,y0,x1,y1` in the units of the screenshot named by `screenshot_id` (else 1280x720). Returned at full resolution.",
      "schema": {
       "type": "string",
       "maxLength": 19,
       "pattern": "^[0-9]{1,4},[0-9]{1,4},[0-9]{1,4},[0-9]{1,4}$"
      }
     },
     {
      "name": "screenshot_id",
      "in": "query",
      "required": false,
      "description": "With `region`: the screenshot whose size the region is measured in.",
      "schema": {
       "type": "string",
       "maxLength": 25,
       "pattern": "^shot_[0-9a-z]{20}$"
      }
     },
     {
      "name": "max_upscale",
      "in": "query",
      "required": false,
      "description": "With `region`: `fit` (default) or a number such as 2 to cap the upscale.",
      "schema": {
       "type": "string",
       "maxLength": 4,
       "pattern": "^(fit|[1-8](\\.[0-9])?)$",
       "default": "fit"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Draw the pointer.",
      "schema": {
       "type": "boolean",
       "default": true
      }
     },
     {
      "name": "settle",
      "in": "query",
      "required": false,
      "description": "true: wait until the screen is quiet (300 ms, 2 s cap) before capturing. Default: capture now.",
      "schema": {
       "type": "boolean",
       "default": false
      }
     },
     {
      "name": "windows",
      "in": "query",
      "required": false,
      "description": "true: also list the open windows (title, app, place, which is active) in `windows`.",
      "schema": {
       "type": "boolean",
       "default": false
      }
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Cache-Control": {
        "description": "Always `no-store`.",
        "schema": {
         "type": "string",
         "enum": [
          "no-store"
         ],
         "maxLength": 8
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Screenshot"
        },
        "examples": {
         "png": {
          "value": {
           "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
           "mime_type": "image/png",
           "width": 1280,
           "height": 720,
           "screen_width": 1920,
           "screen_height": 1080,
           "region": null,
           "captured_at": "2026-10-22T09:14:07Z",
           "input_seq": 41,
           "settle_ms": null,
           "settled": null,
           "quality": null,
           "size_bytes": 241664,
           "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
          }
         },
         "webp": {
          "value": {
           "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
           "mime_type": "image/webp",
           "width": 1280,
           "height": 720,
           "screen_width": 1920,
           "screen_height": 1080,
           "region": null,
           "captured_at": "2026-10-22T09:14:07Z",
           "input_seq": 41,
           "settle_ms": null,
           "settled": null,
           "quality": 80,
           "size_bytes": 98304,
           "data": "UklGRmQ+AQBXRUJQVlA4IFg+AQ..."
          },
          "summary": "Hosted MCP settings"
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/screenshot' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst shot = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").screenshot();\n// shot.data is base64; pass shot.screenshot_id to act()"
     }
    ]
   }
  },
  "/screens/{screen_id}/screenshot/image": {
   "get": {
    "operationId": "getScreenshotImage",
    "summary": "Take a screenshot (image bytes)",
    "description": "Needs the `read` permission. Answers within 5 s.\n\nThe same capture as getScreenshot, returned as image bytes with the metadata in headers. For curl, `<img>` tags and scripts that save files.",
    "tags": [
     "Screenshots"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "name": "size",
      "in": "query",
      "required": false,
      "description": "Image size, WIDTHxHEIGHT or `full`.",
      "schema": {
       "$ref": "#/components/schemas/ShotSize"
      }
     },
     {
      "name": "format",
      "in": "query",
      "required": false,
      "description": "png (default), webp or jpeg.",
      "schema": {
       "$ref": "#/components/schemas/ImageFormat"
      }
     },
     {
      "name": "quality",
      "in": "query",
      "required": false,
      "description": "webp and jpeg quality, 1 to 100.",
      "schema": {
       "type": "integer",
       "format": "int32",
       "minimum": 1,
       "maximum": 100,
       "default": 80
      }
     },
     {
      "name": "max_bytes",
      "in": "query",
      "required": false,
      "description": "Cap on the encoded image: quality steps down, then the image shrinks.",
      "schema": {
       "$ref": "#/components/schemas/MaxBytes"
      }
     },
     {
      "name": "region",
      "in": "query",
      "required": false,
      "description": "Zoom: `x0,y0,x1,y1` in the units of the screenshot named by `screenshot_id` (else 1280x720). Returned at full resolution.",
      "schema": {
       "type": "string",
       "maxLength": 19,
       "pattern": "^[0-9]{1,4},[0-9]{1,4},[0-9]{1,4},[0-9]{1,4}$"
      }
     },
     {
      "name": "screenshot_id",
      "in": "query",
      "required": false,
      "description": "With `region`: the screenshot whose size the region is measured in.",
      "schema": {
       "type": "string",
       "maxLength": 25,
       "pattern": "^shot_[0-9a-z]{20}$"
      }
     },
     {
      "name": "max_upscale",
      "in": "query",
      "required": false,
      "description": "With `region`: `fit` (default) or a number such as 2 to cap the upscale.",
      "schema": {
       "type": "string",
       "maxLength": 4,
       "pattern": "^(fit|[1-8](\\.[0-9])?)$",
       "default": "fit"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Draw the pointer.",
      "schema": {
       "type": "boolean",
       "default": true
      }
     },
     {
      "name": "settle",
      "in": "query",
      "required": false,
      "description": "true: wait until the screen is quiet (300 ms, 2 s cap) before capturing. Default: capture now.",
      "schema": {
       "type": "boolean",
       "default": false
      }
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Kubi-Screenshot-Id": {
        "description": "Screenshot id.",
        "schema": {
         "type": "string",
         "maxLength": 25
        }
       },
       "Kubi-Image-Size": {
        "description": "WIDTHxHEIGHT of the image.",
        "schema": {
         "type": "string",
         "maxLength": 9
        }
       },
       "Kubi-Screen-Size": {
        "description": "WIDTHxHEIGHT of the desktop.",
        "schema": {
         "type": "string",
         "maxLength": 9
        }
       },
       "Kubi-Input-Seq": {
        "description": "Input actions run before the capture.",
        "schema": {
         "type": "integer",
         "format": "int64",
         "minimum": 0,
         "maximum": 9007199254740992
        }
       },
       "Cache-Control": {
        "description": "Always `no-store`.",
        "schema": {
         "type": "string",
         "enum": [
          "no-store"
         ],
         "maxLength": 8
        }
       }
      },
      "content": {
       "image/png": {
        "schema": {
         "type": "string",
         "maxLength": 40000000,
         "format": "binary"
        }
       },
       "image/webp": {
        "schema": {
         "type": "string",
         "maxLength": 40000000,
         "format": "binary",
         "description": "Image bytes."
        }
       },
       "image/jpeg": {
        "schema": {
         "type": "string",
         "maxLength": 40000000,
         "format": "binary",
         "description": "Image bytes."
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/screenshot/image' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -o shot.png"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst png = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").screenshotImage(); // Uint8Array"
     }
    ]
   }
  },
  "/screens/{screen_id}/actions": {
   "post": {
    "operationId": "runActions",
    "summary": "Run a batch of actions",
    "description": "Needs the `control` permission. Answers within 28 s.\n\nRuns 1 to 20 input actions in order, 100 ms apart: click, move, drag, scroll, type, key, wait, settle, zoom and more. Stops at the first failure (status `partial`, later actions `not_executed`). With `screenshot: after` the reply carries the screen after the last action that ran, once it settles, so one call is one agent step.\n\nCoordinates are in `space`: by default the size of the screenshot named by `screenshot_id`, else 1280x720. Points outside are rejected, never clamped. With `screenshot_id`, nothing runs if any input ran after that shot (a person's in the live view included) or the screen restarted (409 STALE_OBSERVATION).\n\nThere is no lock between credentials: any credential with `control` may act at any time. Input on one screen runs one call at a time, in the order calls arrive, while commands run side by side. While a person has control of the screen, calls get 409 CONTROL_HELD; a person taking control mid-batch stops it at the next action (status `partial`, later actions `not_executed`). The optional `Kubi-Run` header groups calls into a run in the screen's history.\n\nSend an Idempotency-Key: after a lost reply, resend once with the same key. Within 10 minutes you get the stored per-action results and a fresh screenshot (`Idempotent-Replayed: true`), never a second click. If the screen restarted in between, 504 OUTCOME_UNKNOWN: look, never repeat blindly.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ActionsRequest"
       },
       "examples": {
        "click": {
         "value": {
          "actions": [
           {
            "type": "click",
            "x": 450,
            "y": 280
           }
          ],
          "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
          "screenshot": "after"
         },
         "summary": "One click, look after"
        },
        "batch": {
         "value": {
          "actions": [
           {
            "type": "click",
            "x": 640,
            "y": 52
           },
           {
            "type": "settle"
           },
           {
            "type": "type",
            "text": "quarterly report",
            "enter": true
           }
          ],
          "space": [
           1280,
           720
          ],
          "screenshot": "after",
          "screenshot_options": {
           "format": "webp",
           "max_bytes": 105000
          }
         },
         "summary": "Search: click, settle, type"
        },
        "gemini": {
         "value": {
          "actions": [
           {
            "type": "click",
            "x": 352,
            "y": 389
           }
          ],
          "space": [
           1000,
           1000
          ],
          "screenshot": "after"
         },
         "summary": "Gemini 0-999 grid"
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "click",
             "status": "ok",
             "duration_ms": 41,
             "cursor": [
              450,
              280
             ],
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             }
            }
           ],
           "active_window": {
            "title": "Save As",
            "app": "org.gnome.TextEditor"
           },
           "windows_opened": [
            {
             "title": "Save As",
             "app": "org.gnome.TextEditor"
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE, BATCH_TOO_LONG."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: SECRET_BLOCKED, IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/actions' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"actions\": [{\"type\": \"click\", \"x\": 450, \"y\": 280}], \"screenshot_id\": \"shot_01jb7q2x9k4m8q1w3z5t\", \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst s = client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\");\nconst shot = await s.screenshot();\nconst r = await s.act(\n  [{ type: \"click\", x: 450, y: 280 }],\n  { screenshot_id: shot.screenshot_id, screenshot: \"after\" },\n);"
     }
    ]
   }
  },
  "/screens/{screen_id}/click": {
   "post": {
    "operationId": "click",
    "summary": "Click",
    "description": "Needs the `control` permission. Answers within 10 s.\n\nClicks at `x`, `y`, or at the pointer when both are left out. `button` picks left, right or middle; `count: 2` is a double click and `count: 3` a triple click. `modifiers` holds keys down while clicking.\n\nWorks like a batch of one: `screenshot: after` returns the screen once it settles, `screenshot_id` refuses to act on a stale screen (409 STALE_OBSERVATION), and resending with the same Idempotency-Key returns the stored result instead of acting twice. For several steps in one round trip, use runActions.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ClickRequest"
       },
       "examples": {
        "click": {
         "value": {
          "x": 450,
          "y": 280,
          "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
          "screenshot": "after"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "click",
             "status": "ok",
             "duration_ms": 41,
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             }
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/click' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"x\": 450, \"y\": 280, \"screenshot_id\": \"shot_01jb7q2x9k4m8q1w3z5t\", \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst r = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").click({ x: 450, y: 280, screenshot: \"after\" });"
     }
    ]
   }
  },
  "/screens/{screen_id}/type": {
   "post": {
    "operationId": "typeText",
    "summary": "Type text",
    "description": "Needs the `control` permission. Answers within 28 s.\n\nTypes `text` as keystrokes into whatever has focus, up to 1,000 characters. `enter: true` presses Enter afterwards. The result reports how many characters were delivered.\n\nWorks like a batch of one: `screenshot: after` returns the screen once it settles, `screenshot_id` refuses to act on a stale screen (409 STALE_OBSERVATION), and resending with the same Idempotency-Key returns the stored result instead of acting twice. For several steps in one round trip, use runActions.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TypeRequest"
       },
       "examples": {
        "type": {
         "value": {
          "text": "quarterly report",
          "enter": true,
          "screenshot": "after"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "type",
             "status": "ok",
             "duration_ms": 41,
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             },
             "typed_chars": 17
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: SECRET_BLOCKED, IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/type' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"text\": \"quarterly report\", \"enter\": true, \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").type({ text: \"quarterly report\", enter: true });"
     }
    ]
   }
  },
  "/screens/{screen_id}/press": {
   "post": {
    "operationId": "pressKeys",
    "summary": "Press keys",
    "description": "Needs the `control` permission. Answers within 25 s.\n\nPresses one key or a chord: `enter`, `ctrl+s`, `alt+tab`. `repeat` presses it several times; `hold_ms` keeps it down (up to 20 s).\n\nWorks like a batch of one: `screenshot: after` returns the screen once it settles, `screenshot_id` refuses to act on a stale screen (409 STALE_OBSERVATION), and resending with the same Idempotency-Key returns the stored result instead of acting twice. For several steps in one round trip, use runActions.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PressRequest"
       },
       "examples": {
        "press": {
         "value": {
          "keys": "ctrl+s",
          "screenshot": "after"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "key",
             "status": "ok",
             "duration_ms": 41,
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             }
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/press' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"keys\": \"ctrl+s\", \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").press({ keys: \"ctrl+s\" });"
     }
    ]
   }
  },
  "/screens/{screen_id}/scroll": {
   "post": {
    "operationId": "scroll",
    "summary": "Scroll",
    "description": "Needs the `control` permission. Answers within 10 s.\n\nScrolls at `x`, `y`, or at the pointer. Positive `dy` scrolls down and positive `dx` right, in wheel notches (or pixels with `unit: px`, 100 px per notch).\n\nWorks like a batch of one: `screenshot: after` returns the screen once it settles, `screenshot_id` refuses to act on a stale screen (409 STALE_OBSERVATION), and resending with the same Idempotency-Key returns the stored result instead of acting twice. For several steps in one round trip, use runActions.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ScrollRequest"
       },
       "examples": {
        "scroll": {
         "value": {
          "x": 640,
          "y": 400,
          "dy": 5,
          "screenshot": "after"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "scroll",
             "status": "ok",
             "duration_ms": 41,
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             }
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/scroll' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"x\": 640, \"y\": 400, \"dy\": 5, \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").scroll({ x: 640, y: 400, dy: 5 });"
     }
    ]
   }
  },
  "/screens/{screen_id}/drag": {
   "post": {
    "operationId": "drag",
    "summary": "Drag",
    "description": "Needs the `control` permission. Answers within 10 s.\n\nPresses at the first point of `path`, moves through the rest and releases at the last: for sliders, selections and moving files or windows.\n\nWorks like a batch of one: `screenshot: after` returns the screen once it settles, `screenshot_id` refuses to act on a stale screen (409 STALE_OBSERVATION), and resending with the same Idempotency-Key returns the stored result instead of acting twice. For several steps in one round trip, use runActions.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DragRequest"
       },
       "examples": {
        "drag": {
         "value": {
          "path": [
           [
            300,
            400
           ],
           [
            900,
            400
           ]
          ],
          "screenshot": "after"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "drag",
             "status": "ok",
             "duration_ms": 41,
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             }
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/drag' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"path\": [[300, 400], [900, 400]], \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").drag({ path: [[300, 400], [900, 400]] });"
     }
    ]
   }
  },
  "/screens/{screen_id}/move": {
   "post": {
    "operationId": "movePointer",
    "summary": "Move the pointer",
    "description": "Needs the `control` permission. Answers within 10 s.\n\nMoves the pointer to `x`, `y` without clicking, to open hover menus and tooltips.\n\nWorks like a batch of one: `screenshot: after` returns the screen once it settles, `screenshot_id` refuses to act on a stale screen (409 STALE_OBSERVATION), and resending with the same Idempotency-Key returns the stored result instead of acting twice. For several steps in one round trip, use runActions.",
    "tags": [
     "Actions"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MoveRequest"
       },
       "examples": {
        "move": {
         "value": {
          "x": 640,
          "y": 52,
          "screenshot": "after"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ActionsResponse"
        },
        "examples": {
         "done": {
          "value": {
           "status": "completed",
           "space": [
            1280,
            720
           ],
           "screenshot_unchanged": false,
           "results": [
            {
             "index": 0,
             "type": "move",
             "status": "ok",
             "duration_ms": 41,
             "screen_changed": true,
             "window": {
              "title": "Files",
              "app": "org.gnome.Nautilus"
             }
            }
           ],
           "screenshot": {
            "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
            "mime_type": "image/png",
            "width": 1280,
            "height": 720,
            "screen_width": 1920,
            "screen_height": 1080,
            "region": null,
            "captured_at": "2026-10-22T09:14:07Z",
            "input_seq": 41,
            "settle_ms": 320,
            "settled": true,
            "quality": null,
            "size_bytes": 241664,
            "data": "iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAAD..."
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, STALE_OBSERVATION, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/move' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"x\": 640, \"y\": 52, \"screenshot\": \"after\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").move({ x: 640, y: 52 });"
     }
    ]
   }
  },
  "/screens/{screen_id}/exec": {
   "post": {
    "operationId": "startExec",
    "summary": "Run a command",
    "description": "Needs the `exec` permission. Answers within 28 s.\n\nStarts a command on the screen. If it ends within `wait_s` (default 25) the reply is 200 with exit code and output (`max_bytes` and `tail` shape it). Otherwise 202 with the exec id: poll getExec. `timeout_s: 0` runs with no limit (dev servers, bots). The command keeps running if the connection drops; resend with the same Idempotency-Key and you get the same exec, never a second run.",
    "tags": [
     "Commands"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ExecCreate"
       },
       "examples": {
        "short": {
         "value": {
          "command": "node --version"
         },
         "summary": "Short, answered inline"
        },
        "long": {
         "value": {
          "argv": [
           "npm",
           "ci"
          ],
          "cwd": "/home/user/app",
          "timeout_s": 1800,
          "wait_s": 25,
          "max_bytes": 8192,
          "tail": true
         },
         "summary": "Long, returns an id"
        },
        "server": {
         "value": {
          "command": "npm run dev",
          "cwd": "/home/user/app",
          "timeout_s": 0,
          "wait_s": 5
         },
         "summary": "Dev server, no time limit"
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished within wait_s.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Exec"
        },
        "examples": {
         "done": {
          "value": {
           "id": "ex_01jb7r2m5k8q3w9x4z6t",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "status": "exited",
           "exit_code": 0,
           "signal": null,
           "started_at": "2026-10-22T09:14:07Z",
           "ended_at": "2026-10-22T09:14:07Z",
           "duration_ms": 412,
           "stdout": "v22.22.0\n",
           "stderr": "",
           "stdout_bytes": 9,
           "stderr_bytes": 0,
           "output_dropped": false,
           "incarnation": 3
          }
         }
        }
       }
      }
     },
     "202": {
      "description": "Still running. Poll getExec.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Location": {
        "$ref": "#/components/headers/Location"
       },
       "Retry-After": {
        "$ref": "#/components/headers/RetryAfter"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Exec"
        },
        "examples": {
         "running": {
          "value": {
           "id": "ex_01jb7r2m5k8q3w9x4z6t",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "status": "running",
           "exit_code": null,
           "signal": null,
           "started_at": "2026-10-22T09:14:07Z",
           "ended_at": null,
           "duration_ms": null,
           "stdout": "added 812 packages in 25s\n",
           "stderr": "",
           "stdout_bytes": 26,
           "stderr_bytes": 0,
           "output_dropped": false,
           "incarnation": 3
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: SECRET_BLOCKED, IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/exec' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"command\": \"node --version\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst run = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").exec({ command: \"node --version\" });\nconsole.log(run.status, run.stdout);"
     }
    ]
   }
  },
  "/screens/{screen_id}/exec/{exec_id}": {
   "get": {
    "operationId": "getExec",
    "summary": "Get a command's status and output",
    "description": "Needs the `exec` permission. Answers within 28 s.\n\nStatus, exit code and output from byte offsets. With `wait_s`, `until=exit` (default) holds the reply until the command ends; `until=output` answers as soon as it prints (for live log views). Output keeps the last 1 MiB per stream for 24 h after exit.",
    "tags": [
     "Commands"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/ExecId"
     },
     {
      "$ref": "#/components/parameters/WaitS"
     },
     {
      "name": "until",
      "in": "query",
      "required": false,
      "description": "What ends a long poll: `exit` (default) or `output`.",
      "schema": {
       "type": "string",
       "enum": [
        "exit",
        "output"
       ],
       "default": "exit",
       "maxLength": 6
      }
     },
     {
      "name": "stdout_from",
      "in": "query",
      "required": false,
      "description": "Byte offset in stdout to read from (the last reply's stdout_bytes).",
      "schema": {
       "type": "integer",
       "format": "int64",
       "minimum": 0,
       "maximum": 1099511627776,
       "default": 0
      }
     },
     {
      "name": "stderr_from",
      "in": "query",
      "required": false,
      "description": "Byte offset in stderr to read from (the last reply's stderr_bytes).",
      "schema": {
       "type": "integer",
       "format": "int64",
       "minimum": 0,
       "maximum": 1099511627776,
       "default": 0
      }
     },
     {
      "name": "max_bytes",
      "in": "query",
      "required": false,
      "description": "Bytes of output per stream in this reply.",
      "schema": {
       "type": "integer",
       "format": "int32",
       "minimum": 1,
       "maximum": 1048576,
       "default": 65536
      }
     },
     {
      "name": "tail",
      "in": "query",
      "required": false,
      "description": "true: the last max_bytes after the offset. false: the first.",
      "schema": {
       "type": "boolean",
       "default": false
      }
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Exec"
        },
        "examples": {
         "done": {
          "value": {
           "id": "ex_01jb7r2m5k8q3w9x4z6t",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "status": "exited",
           "exit_code": 0,
           "signal": null,
           "started_at": "2026-10-22T09:14:07Z",
           "ended_at": "2026-10-22T09:14:07Z",
           "duration_ms": 31877,
           "stdout": "added 812 packages in 25s\n",
           "stderr": "",
           "stdout_bytes": 26,
           "stderr_bytes": 0,
           "output_dropped": false,
           "incarnation": 3
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/exec/ex_01jb7r2m5k8q3w9x4z6t' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst run = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").execs.get(\"ex_01jb7r2m5k8q3w9x4z6t\", { until: \"exit\", wait_s: 25, tail: true });"
     }
    ]
   }
  },
  "/screens/{screen_id}/exec/{exec_id}/kill": {
   "post": {
    "operationId": "killExec",
    "summary": "Stop a command",
    "description": "Needs the `exec` permission. Answers within 12 s.\n\nSends a signal to the command's process group. TERM then KILL after 10 s.",
    "tags": [
     "Commands"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/ExecId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ExecKill"
       },
       "examples": {
        "term": {
         "value": {
          "signal": "TERM"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Exec"
        },
        "examples": {
         "killed": {
          "value": {
           "id": "ex_01jb7r2m5k8q3w9x4z6t",
           "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
           "status": "killed",
           "exit_code": null,
           "signal": "TERM",
           "started_at": "2026-10-22T09:14:07Z",
           "ended_at": "2026-10-22T09:14:07Z",
           "duration_ms": null,
           "stdout": "added 812 packages in 25s\n",
           "stderr": "",
           "stdout_bytes": 26,
           "stderr_bytes": 0,
           "output_dropped": false,
           "incarnation": 3
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/exec/ex_01jb7r2m5k8q3w9x4z6t/kill' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"signal\": \"TERM\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").execs.kill(\"ex_01jb7r2m5k8q3w9x4z6t\", { signal: \"TERM\" });"
     }
    ]
   }
  },
  "/screens/{screen_id}/files": {
   "put": {
    "operationId": "uploadFile",
    "summary": "Upload a file",
    "description": "Needs the `exec` permission. Answers within 28 s.\n\nWrites the request body to `path` on the screen as the desktop user, creating folders. Replaces an existing file. Up to 64 MiB; bigger files go through exec (curl, git) for now. The 28 s limit counts our work after the body has arrived, not your upload time. The hosted MCP's `write_file` wraps it for files up to 64 KiB.",
    "tags": [
     "Files"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/FilePath"
     },
     {
      "name": "mode",
      "in": "query",
      "required": false,
      "description": "Octal permissions.",
      "schema": {
       "type": "string",
       "maxLength": 4,
       "pattern": "^[0-7]{3,4}$",
       "default": "0644"
      }
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "maxLength": 67108864,
        "format": "binary"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/FileWritten"
        },
        "examples": {
         "written": {
          "value": {
           "path": "/home/user/data/input.csv",
           "size_bytes": 18234,
           "sha256": "9f2c4e1a7b3d5f6e8a0c2b4d6f8e1a3c5b7d9f0e2a4c6b8d0f1e3a5c7b9d1f2e"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "413": {
      "$ref": "#/components/responses/PayloadTooLarge",
      "description": "Too large: PAYLOAD_TOO_LARGE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X PUT 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/files' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H 'Content-Type: application/octet-stream' --data-binary @input.csv"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").files.upload(\"/home/user/data/input.csv\", await readFile(\"input.csv\"));"
     }
    ]
   },
   "get": {
    "operationId": "downloadFile",
    "summary": "Download a file",
    "description": "Needs the `exec` permission. Answers within 28 s.\n\nReturns the file at `path` as bytes. Up to 64 MiB. Needs `exec` (a screen holds logins and keys). The hosted MCP's `read_file` wraps it for files up to 64 KiB.",
    "tags": [
     "Files"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/FilePath"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Kubi-File-Sha256": {
        "description": "SHA-256 of the file.",
        "schema": {
         "type": "string",
         "maxLength": 64
        }
       }
      },
      "content": {
       "application/octet-stream": {
        "schema": {
         "type": "string",
         "maxLength": 67108864,
         "format": "binary"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, FILE_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "413": {
      "$ref": "#/components/responses/PayloadTooLarge",
      "description": "Too large: PAYLOAD_TOO_LARGE."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/files' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -o report.pdf"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst bytes = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").files.download(\"/home/user/report.pdf\");"
     }
    ]
   }
  },
  "/screens/{screen_id}/dir": {
   "get": {
    "operationId": "listFiles",
    "summary": "List a folder",
    "description": "Needs the `exec` permission. Answers within 5 s.\n\nNames, types, sizes and dates in one folder, sorted by name, folders first. Moving and deleting go through exec.",
    "tags": [
     "Files"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "name": "path",
      "in": "query",
      "required": true,
      "description": "Folder on the screen, absolute or relative to /home/user.",
      "schema": {
       "type": "string",
       "maxLength": 1024,
       "minLength": 1
      },
      "example": "/home/user/Downloads"
     },
     {
      "$ref": "#/components/parameters/Limit"
     },
     {
      "$ref": "#/components/parameters/After"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/FileList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "name": "invoices",
             "type": "dir",
             "size_bytes": 4096,
             "modified_at": "2026-10-22T09:14:07Z"
            },
            {
             "name": "report.pdf",
             "type": "file",
             "size_bytes": 182344,
             "modified_at": "2026-10-22T08:51:30Z"
            }
           ],
           "next": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, FILE_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/dir?path=/home/user/Downloads' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { data } = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").files.list(\"/home/user/Downloads\");"
     }
    ]
   }
  },
  "/screens/{screen_id}/clipboard": {
   "get": {
    "operationId": "getClipboard",
    "summary": "Read the clipboard",
    "description": "Needs the `control` permission. Answers within 5 s.\n\nThe clipboard's text, up to 100,000 characters; `truncated` says when there was more. Text only: an image or files on the clipboard read as empty. Needs `control`, since clipboards hold copied passwords. History records the call, never the text read.",
    "tags": [
     "Clipboard"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Clipboard"
        },
        "examples": {
         "text": {
          "value": {
           "text": "INV-2026-0419",
           "truncated": false
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/clipboard' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { text } = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").clipboard.get();"
     }
    ]
   },
   "put": {
    "operationId": "setClipboard",
    "summary": "Set the clipboard",
    "description": "Needs the `control` permission. Answers within 5 s.\n\nPuts text on the clipboard, up to 100,000 characters, for the screen's apps to paste; empty clears it. To paste it straight into the focused field, use the `type` action with `via: paste`. The text stays until something else copies or the screen restarts. History keeps it as typed text.",
    "tags": [
     "Clipboard"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ClipboardSet"
       },
       "examples": {
        "text": {
         "value": {
          "text": "Quarterly report, Q3 2026"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Clipboard"
        },
        "examples": {
         "text": {
          "value": {
           "text": "Quarterly report, Q3 2026",
           "truncated": false
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: CONTROL_HELD, SCREEN_NOT_RUNNING."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: SECRET_BLOCKED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     },
     "504": {
      "$ref": "#/components/responses/OutcomeUnknown",
      "description": "Outcome unknown: OUTCOME_UNKNOWN."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X PUT 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/clipboard' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"text\": \"Quarterly report, Q3 2026\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nawait client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").clipboard.set({ text: \"Quarterly report, Q3 2026\" });"
     }
    ]
   }
  },
  "/screens/{screen_id}/viewer": {
   "post": {
    "operationId": "createViewer",
    "summary": "Get a live-view link",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nA link to watch the screen live in a browser. Open it within 60 s; single use. API keys and apps get view-only links (to show a person what is happening) whose session ends after 60 minutes; a signed-in person gets an interactive one, where Take control turns on the mouse and keyboard. While a person has control, calls that look at or act on the screen get 409 CONTROL_HELD, and the person's input makes older screenshots stale. Revoking the credential that minted a link closes its session.",
    "tags": [
     "Live view"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ViewerCreate"
       },
       "examples": {
        "view": {
         "value": {
          "mode": "view"
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Created.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Idempotent-Replayed": {
        "$ref": "#/components/headers/IdempotentReplayed"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Viewer"
        },
        "examples": {
         "view": {
          "value": {
           "url": "https://console.trykubi.com/screens/scr_7f2kq9w3x8m4b6tzp1ac/live#t=vt_4Hq8mZ2kP9xW3nR7bT5c",
           "mode": "view",
           "expires_at": "2026-10-22T09:15:07Z",
           "session_expires_at": "2026-10-22T10:15:07Z"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED, HUMAN_ONLY."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: SCREEN_NOT_RUNNING, IDEMPOTENCY_KEY_IN_USE."
     },
     "415": {
      "$ref": "#/components/responses/UnsupportedMediaType",
      "description": "Unsupported media type: UNSUPPORTED_MEDIA_TYPE."
     },
     "422": {
      "$ref": "#/components/responses/Unprocessable",
      "description": "Unprocessable: IDEMPOTENCY_KEY_REUSED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl -X POST 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/viewer' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"mode\": \"view\"}'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { url } = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").viewerLink();"
     }
    ]
   },
   "get": {
    "operationId": "openViewer",
    "summary": "Open the live view (WebSocket)",
    "description": "No key needed. Answers within 5 s.\n\nThe stream a live-view link's page opens: a WebSocket carrying VNC (RFB 3.8) frames, for noVNC or any RFB client. The link's ticket (`vt_...`, from the `#t=` part of its URL) is the credential: send `Sec-WebSocket-Protocol: kubi.v1, <ticket>`, never the ticket in the URL. The reply picks `kubi.v1`. A ticket opens one socket, within 60 s of its link being made. View-only sessions drop keyboard, mouse and clipboard; an interactive one passes them only while its person holds the screen. At most 3 live views per screen. The socket closes when the session ends, the screen stops or restarts, or the credential that made the link is revoked.",
    "tags": [
     "Live view"
    ],
    "security": [],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "name": "Sec-WebSocket-Protocol",
      "in": "header",
      "required": true,
      "description": "`kubi.v1` and the link's ticket, comma separated.",
      "schema": {
       "type": "string",
       "maxLength": 64,
       "pattern": "^kubi\\.v1, ?vt_[A-Za-z0-9_-]{20}$"
      },
      "example": "kubi.v1, vt_4Hq8mZ2kP9xW3nR7bT5c"
     }
    ],
    "responses": {
     "101": {
      "description": "Switching protocols to the WebSocket.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "409": {
      "$ref": "#/components/responses/Conflict",
      "description": "Conflict with the screen's state: SCREEN_NOT_RUNNING."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "503": {
      "$ref": "#/components/responses/Unavailable",
      "description": "Screen unavailable: SCREEN_UNAVAILABLE."
     }
    },
    "x-codeSamples": [
     {
      "lang": "JavaScript",
      "label": "Browser (noVNC)",
      "source": "import RFB from \"@novnc/novnc\";\nconst ticket = new URLSearchParams(new URL(viewer.url).hash.slice(1)).get(\"t\");\nconst rfb = new RFB(el, \"wss://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/viewer\", { wsProtocols: [\"kubi.v1\", ticket] });\nrfb.viewOnly = true;"
     }
    ]
   }
  },
  "/screens/{screen_id}/history": {
   "get": {
    "operationId": "listHistory",
    "summary": "Read the screen's history",
    "description": "Needs the `read` permission. Answers within 28 s.\n\nEvery call made on the screen, newest first: what was called, its parameters and typed text, which key or app called it, its run label, the result, how long it took and the screenshot it returned. Changes to the screen are rows too. Kept 30 days. With detailed history off, rows keep only what was called, by whom, when and the result. The live view is never recorded, and typed text that matches an organization secret is replaced. Rows are written after the reply, never on the call's path. To follow live, pass `since` (a row id) with `wait_s` and get newer rows oldest first.",
    "tags": [
     "History"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/Limit"
     },
     {
      "$ref": "#/components/parameters/After"
     },
     {
      "$ref": "#/components/parameters/WaitS"
     },
     {
      "name": "since",
      "in": "query",
      "required": false,
      "description": "Follow mode: return rows newer than this id, oldest first.",
      "schema": {
       "type": "string",
       "maxLength": 30,
       "pattern": "^evt_[0-9a-z]{20}$"
      }
     },
     {
      "name": "run",
      "in": "query",
      "required": false,
      "description": "Only rows with this `Kubi-Run` label.",
      "schema": {
       "type": "string",
       "maxLength": 100
      }
     },
     {
      "name": "actor",
      "in": "query",
      "required": false,
      "description": "Only rows by this kind of actor.",
      "schema": {
       "type": "string",
       "enum": [
        "api_key",
        "app",
        "person",
        "system"
       ],
       "maxLength": 10
      }
     },
     {
      "name": "errors_only",
      "in": "query",
      "required": false,
      "description": "Only rows whose result is an error.",
      "schema": {
       "type": "boolean",
       "default": false
      }
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HistoryList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "id": "evt_01jb7q0x9v2k8m4n6p3s",
             "at": "2026-10-22T09:14:07Z",
             "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "kind": "call",
             "operation": "runActions",
             "actor": {
              "type": "api_key",
              "credential_id": "key_01jb6m3k9q2w8x4p7n5t",
              "credential_name": "CI runner",
              "user_id": null
             },
             "run": "6f1c2a90-3d4b-4e8f-9a1c-7b2d5e8f0a13",
             "summary": "click (450, 280); type 16 chars; key enter",
             "params": {
              "actions": [
               {
                "type": "click",
                "x": 450,
                "y": 280
               },
               {
                "type": "type",
                "text": "quarterly report"
               },
               {
                "type": "key",
                "keys": "enter"
               }
              ],
              "screenshot": "after"
             },
             "result": "ok",
             "duration_ms": 612,
             "screenshot_id": "shot_01jb7q2x9k4m8q1w3z5t",
             "screenshot_saved": true,
             "request_id": "req_01jb7q0x9v2k8m4n6p3r"
            },
            {
             "id": "evt_01jb7q0x9v2k8m4n6p3t",
             "at": "2026-10-22T09:12:03Z",
             "screen_id": "scr_7f2kq9w3x8m4b6tzp1ac",
             "kind": "proxy.set",
             "operation": null,
             "actor": {
              "type": "person",
              "credential_id": null,
              "credential_name": null,
              "user_id": "usr_01jb5k2m8q3w9x4p6n7a"
             },
             "run": null,
             "summary": "Arthur set the proxy",
             "params": null,
             "result": "ok",
             "duration_ms": null,
             "screenshot_id": null,
             "screenshot_saved": false,
             "request_id": null
            }
           ],
           "next": "c2luY2U6ZXZ0XzAx"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/history' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst { data } = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").history.list({ limit: 20 });"
     }
    ]
   }
  },
  "/screens/{screen_id}/screenshots/{screenshot_id}": {
   "get": {
    "operationId": "getSavedScreenshot",
    "summary": "Get a saved screenshot",
    "description": "Needs the `read` permission. Answers within 5 s.\n\nThe exact image a call returned, from the screen's history, as image bytes. Kept 30 days, then 404 NOT_FOUND. Nothing is saved while detailed history is off.",
    "tags": [
     "History"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "#/components/parameters/ScreenId"
     },
     {
      "$ref": "#/components/parameters/ScreenshotId"
     },
     {
      "$ref": "#/components/parameters/Run"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       },
       "Kubi-Image-Size": {
        "description": "WIDTHxHEIGHT of the image.",
        "schema": {
         "type": "string",
         "maxLength": 9
        }
       },
       "Kubi-Screen-Size": {
        "description": "WIDTHxHEIGHT of the desktop.",
        "schema": {
         "type": "string",
         "maxLength": 9
        }
       }
      },
      "content": {
       "image/png": {
        "schema": {
         "type": "string",
         "maxLength": 40000000,
         "format": "binary"
        }
       },
       "image/webp": {
        "schema": {
         "type": "string",
         "maxLength": 40000000,
         "format": "binary",
         "description": "Image bytes."
        }
       },
       "image/jpeg": {
        "schema": {
         "type": "string",
         "maxLength": 40000000,
         "format": "binary",
         "description": "Image bytes."
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden",
      "description": "Not allowed: PERMISSION_DENIED."
     },
     "404": {
      "$ref": "#/components/responses/NotFound",
      "description": "Not found: SCREEN_NOT_FOUND, NOT_FOUND."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/screens/scr_7f2kq9w3x8m4b6tzp1ac/screenshots/shot_01jb7q2x9k4m8q1w3z5t' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\" \\\n  -o shot.png"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst png = await client.screen(\"scr_7f2kq9w3x8m4b6tzp1ac\").history.screenshot(\"shot_01jb7q2x9k4m8q1w3z5t\"); // Uint8Array"
     }
    ]
   }
  },
  "/me": {
   "get": {
    "operationId": "getMe",
    "summary": "Who am I",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nThe organization, the credential's permissions and screen binding, its screens and screen cap, billing state and limits. The hosted MCP turns this into its server instructions.",
    "tags": [
     "Account"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Me"
        },
        "examples": {
         "api_key": {
          "value": {
           "organization": {
            "id": "org_01jb5k2m8q3w9x4p6n7t",
            "name": "Arthur"
           },
           "user": null,
           "credential": {
            "type": "api_key",
            "id": "key_01jb6m3k9q2w8x4p7n5t",
            "name": "CI runner",
            "permissions": [
             "read",
             "control",
             "exec"
            ],
            "screen_ids": [
             "scr_7f2kq9w3x8m4b6tzp1ac"
            ],
            "expires_at": "2027-01-20T09:14:07Z"
           },
           "screens": {
            "count": 2,
            "cap": 3
           },
           "templates": {
            "count": 1,
            "cap": 5
           },
           "billing": {
            "state": "ok",
            "credit_state": "ok",
            "warning": null,
            "manage_url": "https://console.trykubi.com/settings/billing"
           },
           "limits": {
            "actions_per_call": 20,
            "images_per_reply": 4,
            "call_time_limit_s": 30,
            "file_max_bytes": 67108864,
            "requests_per_minute": 300,
            "image_calls_per_second": 2,
            "calls_per_second": 10
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/me' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst me = await client.me();"
     }
    ]
   }
  },
  "/openapi.json": {
   "get": {
    "operationId": "getOpenApi",
    "summary": "This spec",
    "description": "No key needed. Answers within 2 s.\n\nThe public OpenAPI file, generated without the dashboard-only routes. No credential needed.",
    "tags": [
     "Account"
    ],
    "security": [],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "maxProperties": 20,
         "additionalProperties": true
        },
        "examples": {
         "head": {
          "value": {
           "openapi": "3.1.2",
           "info": {
            "title": "Kubi API",
            "version": "2026-10-22"
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/openapi.json'"
     },
     {
      "lang": "TypeScript",
      "label": "TypeScript SDK",
      "source": "import { Kubi } from \"@trykubi/sdk\";\nconst client = new Kubi(); // reads KUBI_API_KEY\nconst spec = await client.openApi();"
     }
    ]
   }
  },
  "/secrets": {
   "get": {
    "operationId": "listSecrets",
    "summary": "List secrets",
    "description": "Needs the `read` permission. Answers within 2 s.\n\nNames and the screens they apply to, never values.",
    "tags": [
     "Secrets"
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "headers": {
       "Request-Id": {
        "$ref": "#/components/headers/RequestId"
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       },
       "Billing-Warning": {
        "$ref": "#/components/headers/BillingWarning"
       },
       "Deprecation": {
        "$ref": "#/components/headers/Deprecation"
       },
       "Sunset": {
        "$ref": "#/components/headers/Sunset"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/SecretList"
        },
        "examples": {
         "page": {
          "value": {
           "data": [
            {
             "name": "STRIPE_TEST_KEY",
             "screen_ids": [
              "scr_7f2kq9w3x8m4b6tzp1ac"
             ],
             "updated_at": "2026-10-22T09:14:07Z"
            }
           ],
           "next": null
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest",
      "description": "Bad request: INVALID_ARGUMENT."
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized",
      "description": "Not authenticated: UNAUTHENTICATED."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests",
      "description": "Rate limited: RATE_LIMITED."
     },
     "500": {
      "$ref": "#/components/responses/InternalError",
      "description": "Internal error: INTERNAL."
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "label": "curl",
      "source": "curl 'https://api.trykubi.com/v1/secrets' \\\n  -H \"Authorization: Bearer $KUBI_API_KEY\""
     }
    ]
   }
  }
 },
 "components": {
  "schemas": {
   "ErrorCode": {
    "anyOf": [
     {
      "type": "string",
      "enum": [
       "INVALID_ARGUMENT",
       "COORDINATE_OUT_OF_RANGE",
       "BATCH_TOO_LONG",
       "UNAUTHENTICATED",
       "QUOTA_EXCEEDED",
       "PAYMENT_REQUIRED",
       "CREDIT_EXHAUSTED",
       "SUBSCRIPTION_REQUIRED",
       "PERMISSION_DENIED",
       "HUMAN_ONLY",
       "CSRF_REJECTED",
       "ORGANIZATION_SUSPENDED",
       "ACCOUNT_PENDING",
       "SCREEN_NOT_FOUND",
       "NOT_FOUND",
       "FILE_NOT_FOUND",
       "NO_CAPACITY",
       "STALE_OBSERVATION",
       "CONTROL_HELD",
       "SCREEN_NOT_RUNNING",
       "OPERATION_IN_PROGRESS",
       "IDEMPOTENCY_KEY_IN_USE",
       "UNSUPPORTED_CAPABILITY",
       "PAYLOAD_TOO_LARGE",
       "UNSUPPORTED_MEDIA_TYPE",
       "IDEMPOTENCY_KEY_REUSED",
       "SECRET_BLOCKED",
       "URL_NOT_ALLOWED",
       "RATE_LIMITED",
       "INTERNAL",
       "SCREEN_UNAVAILABLE",
       "OUTCOME_UNKNOWN"
      ],
      "maxLength": 63
     },
     {
      "type": "string",
      "maxLength": 63
     }
    ],
    "title": "ErrorCode",
    "description": "Stable machine code. The list can grow: treat an unknown code by its HTTP status.\n\n- `INVALID_ARGUMENT` (400): A field is missing, malformed or out of range (details.field, details.problem). Fix that field and resend.\n- `COORDINATE_OUT_OF_RANGE` (400): A point lies outside the coordinate space; it is rejected, never clamped. Use a point inside details.space.\n- `BATCH_TOO_LONG` (400): The batch would run past 28 s, inside the 30 s call limit. Split it into smaller batches.\n- `UNAUTHENTICATED` (401): The credential is missing, malformed, unknown, revoked or expired (details.reason). Send a valid key or token.\n- `QUOTA_EXCEEDED` (402): The organization is at its screen cap (details.screen_cap). Delete a screen, or a person asks support for a higher cap.\n- `PAYMENT_REQUIRED` (402): Payment failed. A person pays at details.pay_url.\n- `CREDIT_EXHAUSTED` (402): The usage credit that pays for disposable screens is used up (details.balance_usd). A person tops up at details.billing_url, or create a persistent screen.\n- `SUBSCRIPTION_REQUIRED` (402): Buying usage credit needs a paid persistent screen first. A person subscribes at details.billing_url.\n- `PERMISSION_DENIED` (403): The credential lacks details.needed, or is not bound to this screen. Use a credential that has it.\n- `HUMAN_ONLY` (403): Only a signed-in person (dashboard cookie) may call this. Ask a person to do it in the dashboard.\n- `CSRF_REJECTED` (403): A cookie request changed state without the dashboard's Origin. Send it from the dashboard, or use an API key.\n- `ORGANIZATION_SUSPENDED` (403): The organization is suspended. Stop and tell the user.\n- `ACCOUNT_PENDING` (403): The account is waiting for approval. Stop and tell the user.\n- `SCREEN_NOT_FOUND` (404): No such screen in your organization (the same answer for another organization's id). List screens for a valid id.\n- `NOT_FOUND` (404): No such operation, exec, key, app, export, template, snapshot or saved screenshot (saved ones go after 30 days). Check the id.\n- `FILE_NOT_FOUND` (404): Nothing at that path on the screen. List the folder with listFiles.\n- `NO_CAPACITY` (409): No free machine for a new screen right now; nothing was charged. Retry after retry_after.\n- `STALE_OBSERVATION` (409): Input ran (anyone's, a person's in the live view included) or the screen restarted since your screenshot (details.acted_since, details.restarted). Take a new screenshot first.\n- `CONTROL_HELD` (409): A person has control of this screen, so nothing ran. Control ends when they release it, or after 5 minutes with no input from them. Wait retry_after seconds, then take a new screenshot before acting.\n- `SCREEN_NOT_RUNNING` (409): The screen is stopped, starting or stopping. Start it, or wait on its operation.\n- `OPERATION_IN_PROGRESS` (409): Another power operation runs on this screen. Wait on it, then retry.\n- `IDEMPOTENCY_KEY_IN_USE` (409): A request with this key is still running. Retry the same request.\n- `UNSUPPORTED_CAPABILITY` (409): This screen cannot do that (for example a screenless box). Use a screen that can.\n- `PAYLOAD_TOO_LARGE` (413): The body or file is over details.limit_bytes. Send less, or move big files through exec.\n- `UNSUPPORTED_MEDIA_TYPE` (415): Wrong Content-Type. Send application/json (application/octet-stream for file uploads).\n- `IDEMPOTENCY_KEY_REUSED` (422): This key was used with a different body. Use a new key.\n- `SECRET_BLOCKED` (422): The input contains one of our API keys; nothing was typed or run. Remove the key and resend.\n- `URL_NOT_ALLOWED` (422): The URL is not public HTTPS: it resolves to a private or reserved address, or redirects. Use a public HTTPS URL.\n- `RATE_LIMITED` (429): Too many requests. Wait retry_after seconds.\n- `INTERNAL` (500): Our bug. Retry with the same Idempotency-Key.\n- `SCREEN_UNAVAILABLE` (503): The screen's host or desktop is not answering. Retry after retry_after.\n- `OUTCOME_UNKNOWN` (504): The work may or may not have happened. Look before acting; never resend blindly."
   },
   "ErrorDetails": {
    "type": "object",
    "title": "ErrorDetails",
    "maxProperties": 30,
    "description": "Code-specific facts. Every key is optional and new keys may appear (U6).",
    "properties": {
     "field": {
      "type": "string",
      "maxLength": 200,
      "description": "INVALID_ARGUMENT: the field at fault, as a path (`actions[0].y`)."
     },
     "problem": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "required",
         "unknown_field",
         "malformed",
         "too_long",
         "out_of_range",
         "conflict",
         "private_address"
        ],
        "maxLength": 30
       },
       {
        "type": "string",
        "maxLength": 30
       }
      ],
      "description": "INVALID_ARGUMENT, URL_NOT_ALLOWED: what is wrong."
     },
     "reason": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "missing",
         "malformed",
         "unknown_key",
         "revoked",
         "expired"
        ],
        "maxLength": 20
       },
       {
        "type": "string",
        "maxLength": 20
       }
      ],
      "description": "UNAUTHENTICATED: why the credential failed."
     },
     "needed": {
      "$ref": "#/components/schemas/Permission"
     },
     "has": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Permission"
      },
      "maxItems": 4
     },
     "acted_since": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 2147483647,
      "description": "STALE_OBSERVATION: input actions run since your screenshot."
     },
     "restarted": {
      "type": "boolean",
      "description": "STALE_OBSERVATION: the screen restarted since your screenshot."
     },
     "action_index": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 19
     },
     "x": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 99999
     },
     "y": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 99999
     },
     "space": {
      "type": "array",
      "items": {
       "type": "integer",
       "format": "int32",
       "minimum": 1,
       "maximum": 9999
      },
      "maxItems": 2,
      "minItems": 2
     },
     "screenshot_size": {
      "type": "array",
      "items": {
       "type": "integer",
       "format": "int32",
       "minimum": 1,
       "maximum": 9999
      },
      "maxItems": 2,
      "minItems": 2
     },
     "limit_bytes": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "maximum": 1099511627776
     },
     "screens": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 1000,
      "description": "QUOTA_EXCEEDED: screens the organization has, running or stopped."
     },
     "screen_cap": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 1000,
      "description": "QUOTA_EXCEEDED: the most screens the organization may have."
     },
     "snapshots": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100,
      "description": "QUOTA_EXCEEDED on createSnapshot: the screen's snapshots."
     },
     "snapshot_cap": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100,
      "description": "QUOTA_EXCEEDED on createSnapshot: the most a screen may keep."
     },
     "min_usd": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100000,
      "description": "INVALID_ARGUMENT on a top-up: the smallest allowed now."
     },
     "max_usd": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100000,
      "description": "INVALID_ARGUMENT on a top-up: the largest allowed now."
     },
     "billing_url": {
      "type": "string",
      "maxLength": 300,
      "format": "uri"
     },
     "pay_url": {
      "type": "string",
      "maxLength": 2000,
      "format": "uri"
     },
     "stops_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "balance_usd": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "maximum": 1000000
     }
    }
   },
   "ErrorBody": {
    "type": "object",
    "title": "ErrorBody",
    "description": "The same shape for every error on every route.",
    "required": [
     "code",
     "message",
     "request_id",
     "retryable",
     "outcome"
    ],
    "properties": {
     "code": {
      "$ref": "#/components/schemas/ErrorCode"
     },
     "message": {
      "type": "string",
      "maxLength": 1000,
      "description": "One sentence for people and agents, ending with the fix. Do not parse it."
     },
     "request_id": {
      "type": "string",
      "maxLength": 40,
      "pattern": "^req_[0-9a-z]{20}$",
      "description": "Quote it in support requests."
     },
     "retryable": {
      "type": "boolean",
      "description": "True when the same request may succeed later. The SDK retries only these, never 504."
     },
     "retry_after": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 86400,
        "description": "Seconds to wait before trying again."
       },
       {
        "type": "null"
       }
      ]
     },
     "outcome": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "not_applied",
         "unknown"
        ],
        "maxLength": 20
       },
       {
        "type": "string",
        "maxLength": 20
       }
      ],
      "description": "not_applied: nothing changed, so the reply is never stored for the Idempotency-Key. unknown: look before acting."
     },
     "docs_url": {
      "type": "string",
      "maxLength": 300,
      "format": "uri"
     },
     "details": {
      "$ref": "#/components/schemas/ErrorDetails"
     }
    }
   },
   "Error": {
    "type": "object",
    "title": "Error",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "$ref": "#/components/schemas/ErrorBody"
     }
    }
   },
   "Permission": {
    "type": "string",
    "enum": [
     "read",
     "control",
     "exec",
     "power"
    ],
    "maxLength": 10,
    "title": "Permission",
    "description": "read: see screens, screenshots and history. control: act on the GUI. exec: run commands, read, write and list files. power: create, start, stop, restart (and, for API keys, delete). Same names as OAuth scopes."
   },
   "Coordinate": {
    "type": "integer",
    "format": "int32",
    "minimum": 0,
    "maximum": 9999,
    "description": "Pixels in the request's coordinate space, origin top left.",
    "title": "Coordinate"
   },
   "Point": {
    "type": "array",
    "items": {
     "$ref": "#/components/schemas/Coordinate"
    },
    "maxItems": 2,
    "minItems": 2,
    "title": "Point",
    "description": "[x, y] in the coordinate space."
   },
   "Space": {
    "type": "array",
    "items": {
     "type": "integer",
     "format": "int32",
     "minimum": 1,
     "maximum": 9999
    },
    "maxItems": 2,
    "minItems": 2,
    "title": "Space",
    "description": "[width, height] that coordinates are expressed in. Default: the size of the screenshot named by `screenshot_id`, else [1280, 720] (the default screenshot). Both sent and different: 400 INVALID_ARGUMENT with both sizes in details. Gemini sends [1000, 1000]. Each axis is scaled to the desktop separately."
   },
   "Region": {
    "type": "array",
    "items": {
     "$ref": "#/components/schemas/Coordinate"
    },
    "maxItems": 4,
    "minItems": 4,
    "title": "Region",
    "description": "[x0, y0, x1, y1], top-left and bottom-right corners, in the coordinate space."
   },
   "Labels": {
    "type": "object",
    "title": "Labels",
    "maxProperties": 20,
    "additionalProperties": {
     "type": "string",
     "maxLength": 100
    },
    "propertyNames": {
     "pattern": "^[a-z0-9_.-]{1,40}$",
     "maxLength": 40
    },
    "description": "Your own key-value tags. Filter lists with `label=key:value`."
   },
   "Size": {
    "type": "object",
    "title": "Size",
    "required": [
     "width",
     "height"
    ],
    "properties": {
     "width": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 9999
     },
     "height": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 9999
     }
    }
   },
   "MouseButton": {
    "type": "string",
    "enum": [
     "left",
     "right",
     "middle",
     "back",
     "forward"
    ],
    "default": "left",
    "maxLength": 10,
    "title": "MouseButton"
   },
   "Modifiers": {
    "type": "array",
    "items": {
     "type": "string",
     "enum": [
      "shift",
      "ctrl",
      "alt",
      "super"
     ],
     "maxLength": 5
    },
    "maxItems": 4,
    "uniqueItems": true,
    "title": "Modifiers",
    "description": "Keys held during the action."
   },
   "ImageFormat": {
    "type": "string",
    "enum": [
     "png",
     "webp",
     "jpeg"
    ],
    "default": "png",
    "maxLength": 4,
    "title": "ImageFormat",
    "description": "png by default (lossless, sharp text, no ICC chunk). webp at quality 80 is what the hosted MCP server sends, because it is smaller. jpeg is not recommended for UI text."
   },
   "ShotSize": {
    "type": "string",
    "maxLength": 9,
    "pattern": "^([0-9]{2,4}x[0-9]{2,4}|full)$",
    "default": "1280x720",
    "title": "ShotSize",
    "description": "WIDTHxHEIGHT with the desktop's aspect ratio, or `full` (desktop size)."
   },
   "MaxBytes": {
    "type": "integer",
    "format": "int32",
    "minimum": 1000,
    "maximum": 40000000,
    "title": "MaxBytes",
    "description": "Cap on the encoded image. webp and jpeg step quality 80, 70, 60, 50, then shrink; png only shrinks. The reply says the quality and size used. The hosted MCP sends 105000."
   },
   "MaxUpscale": {
    "title": "MaxUpscale",
    "description": "Zoom only. `fit` (default): scale the region to the usual shot size, as Anthropic does. A number caps the upscale (2 = at most twice the desktop pixels), which costs fewer image tokens.",
    "default": "fit",
    "oneOf": [
     {
      "type": "string",
      "const": "fit",
      "maxLength": 3
     },
     {
      "type": "number",
      "format": "double",
      "minimum": 1,
      "maximum": 8
     }
    ]
   },
   "ScreenState": {
    "anyOf": [
     {
      "type": "string",
      "enum": [
       "provisioning",
       "starting",
       "running",
       "stopping",
       "stopped",
       "restarting",
       "deleting",
       "deleted",
       "failed"
      ],
      "maxLength": 20
     },
     {
      "type": "string",
      "maxLength": 20
     }
    ],
    "title": "ScreenState",
    "description": "Power state. `failed` carries a state_reason. New values may appear."
   },
   "Health": {
    "type": "object",
    "title": "Health",
    "description": "Each capability on its own: a running VM can have a broken display. Host unreachable means unknown.",
    "required": [
     "display",
     "input",
     "exec",
     "viewer"
    ],
    "properties": {
     "display": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "ok",
         "degraded",
         "down",
         "unknown"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "input": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "ok",
         "degraded",
         "down",
         "unknown"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "exec": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "ok",
         "degraded",
         "down",
         "unknown"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "viewer": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "ok",
         "degraded",
         "down",
         "unknown"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "observed_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "Resources": {
    "type": "object",
    "title": "Resources",
    "description": "One size at launch. Resize comes later.",
    "required": [
     "vcpu",
     "memory_mb",
     "disk_gb"
    ],
    "properties": {
     "vcpu": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 64
     },
     "memory_mb": {
      "type": "integer",
      "format": "int32",
      "minimum": 512,
      "maximum": 262144
     },
     "disk_gb": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 4096
     }
    }
   },
   "ControlState": {
    "type": "object",
    "title": "ControlState",
    "description": "Whether a person has control of the screen.",
    "required": [
     "held"
    ],
    "properties": {
     "held": {
      "type": "boolean",
      "description": "A person has control. Calls that look at or act on the screen get 409 CONTROL_HELD."
     },
     "since": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     },
     "releases_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40,
        "description": "When control ends if the person does nothing more. Their input pushes it back."
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "StateReason": {
    "anyOf": [
     {
      "type": "string",
      "enum": [
       "stopped_by_user",
       "stopped_by_billing",
       "stopped_by_admin",
       "stopped_by_credit"
      ],
      "maxLength": 200
     },
     {
      "type": "string",
      "maxLength": 200
     }
    ],
    "title": "StateReason",
    "description": "Why the screen is in its state. A stopped screen says who stopped it: `stopped_by_user` (a credential or a person in the organization), `stopped_by_billing` (the card's retries ended; paying again starts it), `stopped_by_admin` (Kubi support) or `stopped_by_credit` (a disposable screen whose usage credit ran out; a top-up does not start it). A failed screen always carries a short code for what broke. New values may appear: show an unknown one as words."
   },
   "ScreenColor": {
    "type": "string",
    "enum": [
     "green",
     "lime",
     "teal",
     "cyan",
     "blue",
     "indigo",
     "violet",
     "pink",
     "orange",
     "amber"
    ],
    "maxLength": 10,
    "title": "ScreenColor",
    "description": "The colour of the screen's cube, shown beside its name in the dashboard. Only for people telling screens apart; nothing else reads it. A new screen is green unless its create says otherwise."
   },
   "ScreenKind": {
    "type": "string",
    "enum": [
     "persistent",
     "disposable"
    ],
    "default": "persistent",
    "maxLength": 12,
    "title": "ScreenKind",
    "description": "persistent: on the monthly subscription, one price per screen, running or stopped; kept until deleted, and a delete can be undone for 7 days. disposable: paid from usage credit for the time it runs, charged per billing step as each step starts; it can carry an end time, is deleted 7 days after it stops unless started again, and a delete is final."
   },
   "ScreenSpend": {
    "type": "object",
    "title": "ScreenSpend",
    "description": "What a disposable screen has cost so far.",
    "required": [
     "running_s",
     "charged_usd",
     "since"
    ],
    "properties": {
     "running_s": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "maximum": 1099511627776,
      "description": "Seconds it has run as a disposable screen."
     },
     "charged_usd": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "maximum": 1000000,
      "description": "Credit charged for it so far, the step now running included."
     },
     "since": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40,
      "description": "When it became disposable (created or switched)."
     }
    }
   },
   "Screen": {
    "type": "object",
    "title": "Screen",
    "required": [
     "id",
     "name",
     "kind",
     "color",
     "labels",
     "state",
     "desktop",
     "screenshot_default",
     "image",
     "size",
     "resources",
     "region",
     "health",
     "control",
     "incarnation",
     "created_at",
     "updated_at"
    ],
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^scr_[0-9a-z]{20}$",
      "maxLength": 24
     },
     "name": {
      "type": "string",
      "maxLength": 63,
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
     },
     "kind": {
      "$ref": "#/components/schemas/ScreenKind"
     },
     "ends_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40,
        "description": "Disposable only: when it is deleted. null: no end time."
       },
       {
        "type": "null"
       }
      ]
     },
     "delete_after": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40,
        "description": "Disposable and stopped: when it is deleted unless started again or made persistent."
       },
       {
        "type": "null"
       }
      ]
     },
     "template_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^tpl_[0-9a-z]{20}$",
        "maxLength": 24,
        "description": "The template it started from; null: blank."
       },
       {
        "type": "null"
       }
      ]
     },
     "spend": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/ScreenSpend",
        "description": "Disposable only; null for a persistent screen."
       },
       {
        "type": "null"
       }
      ]
     },
     "created_by": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Actor",
        "description": "Who created it: the API key, app or person. Disposable screens are mostly made by agents, so lists show it."
       },
       {
        "type": "null"
       }
      ]
     },
     "color": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "green",
         "lime",
         "teal",
         "cyan",
         "blue",
         "indigo",
         "violet",
         "pink",
         "orange",
         "amber"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ],
      "description": "The cube's colour (ScreenColor). New colours may appear: show an unknown one as green."
     },
     "labels": {
      "$ref": "#/components/schemas/Labels"
     },
     "state": {
      "$ref": "#/components/schemas/ScreenState"
     },
     "state_reason": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/StateReason"
       },
       {
        "type": "null"
       }
      ]
     },
     "desktop": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Size",
        "description": "The desktop's size; null for an image with no desktop, which takes no screenshot or action."
       },
       {
        "type": "null"
       }
      ]
     },
     "screenshot_default": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Size",
        "description": "A screenshot's size when none is asked for; null with no desktop."
       },
       {
        "type": "null"
       }
      ]
     },
     "image": {
      "type": "string",
      "maxLength": 63,
      "description": "The image it was created from. One desktop image at launch."
     },
     "size": {
      "type": "string",
      "maxLength": 20,
      "description": "The size it was sold as (`standard`, the one size at launch); `resources` says what it has."
     },
     "resources": {
      "$ref": "#/components/schemas/Resources"
     },
     "region": {
      "type": "string",
      "maxLength": 20
     },
     "health": {
      "$ref": "#/components/schemas/Health"
     },
     "control": {
      "$ref": "#/components/schemas/ControlState"
     },
     "viewers": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 3,
      "description": "Live views open on the screen right now, at most 3."
     },
     "incarnation": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 2147483647,
      "description": "Goes up when the machine restarts. Old screenshots and exec ids stop applying."
     },
     "operation_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^op_[0-9a-z]{20}$",
        "maxLength": 23
       },
       {
        "type": "null"
       }
      ]
     },
     "last_action_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40,
        "description": "When the last input call (click, type, key and the rest) ran."
       },
       {
        "type": "null"
       }
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "updated_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     }
    }
   },
   "ScreenCreate": {
    "type": "object",
    "title": "ScreenCreate",
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 63,
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
      "description": "Unique in the organization. Default: generated."
     },
     "color": {
      "$ref": "#/components/schemas/ScreenColor"
     },
     "labels": {
      "$ref": "#/components/schemas/Labels"
     },
     "kind": {
      "$ref": "#/components/schemas/ScreenKind"
     },
     "template_id": {
      "type": "string",
      "pattern": "^tpl_[0-9a-z]{20}$",
      "maxLength": 24,
      "description": "Start from this template. Default: blank."
     },
     "size": {
      "type": "string",
      "maxLength": 20,
      "pattern": "^[a-z0-9-]{1,20}$",
      "description": "Default: `standard`, the one size at launch. A size not sold in the screen's kind: INVALID_ARGUMENT."
     },
     "ends_in_s": {
      "type": "integer",
      "format": "int32",
      "minimum": 300,
      "maximum": 2592000,
      "description": "Disposable only: delete it this many seconds after it is created. Default: no end time."
     }
    },
    "additionalProperties": false
   },
   "ScreenUpdate": {
    "type": "object",
    "title": "ScreenUpdate",
    "description": "Only the fields you send change. Labels replace the whole map.",
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 63,
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
     },
     "color": {
      "$ref": "#/components/schemas/ScreenColor"
     },
     "labels": {
      "$ref": "#/components/schemas/Labels"
     },
     "ends_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40,
        "description": "Disposable only: set, move or (null) clear the end time. A persistent screen: 400."
       },
       {
        "type": "null"
       }
      ]
     }
    },
    "additionalProperties": false
   },
   "ScreenList": {
    "type": "object",
    "title": "ScreenList",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Screen"
      },
      "maxItems": 100
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "OperationKind": {
    "anyOf": [
     {
      "type": "string",
      "enum": [
       "screen.create",
       "screen.start",
       "screen.stop",
       "screen.restart",
       "screen.delete",
       "screen.restore",
       "screen.convert",
       "template.create",
       "screen.snapshot",
       "screen.reset"
      ],
      "maxLength": 30
     },
     {
      "type": "string",
      "maxLength": 30
     }
    ],
    "title": "OperationKind"
   },
   "ScreenConvert": {
    "type": "object",
    "title": "ScreenConvert",
    "description": "The kind to switch to.",
    "required": [
     "kind"
    ],
    "properties": {
     "kind": {
      "$ref": "#/components/schemas/ScreenKind"
     }
    },
    "additionalProperties": false
   },
   "Template": {
    "type": "object",
    "title": "Template",
    "description": "A saved screen to start new screens from: its disk, which holds what was installed on it, browser logins done by hand included. It never holds a secret: secrets are kept in the screen's memory and never written to its disk, so no template can carry one.",
    "required": [
     "id",
     "name",
     "state",
     "resources",
     "with_memory",
     "screens_started",
     "created_at"
    ],
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^tpl_[0-9a-z]{20}$",
      "maxLength": 24
     },
     "name": {
      "type": "string",
      "maxLength": 63,
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
     },
     "state": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "saving",
         "ready",
         "failed"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ],
      "description": "saving: being made from its screen."
     },
     "source_screen_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^scr_[0-9a-z]{20}$",
        "maxLength": 24,
        "description": "The screen it was saved from; null once that screen is gone."
       },
       {
        "type": "null"
       }
      ]
     },
     "resources": {
      "$ref": "#/components/schemas/Resources"
     },
     "with_memory": {
      "type": "boolean",
      "description": "true: a screen started from it comes back with the open windows it was saved with. false: it boots from the saved disk."
     },
     "size_bytes": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int64",
        "minimum": 0,
        "maximum": 1125899906842624
       },
       {
        "type": "null"
       }
      ]
     },
     "screens_started": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 2147483647,
      "description": "Screens started from it so far."
     },
     "last_used_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     }
    }
   },
   "TemplateList": {
    "type": "object",
    "title": "TemplateList",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Template"
      },
      "maxItems": 100
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "TemplateCreate": {
    "type": "object",
    "title": "TemplateCreate",
    "required": [
     "screen_id",
     "name"
    ],
    "properties": {
     "screen_id": {
      "type": "string",
      "pattern": "^scr_[0-9a-z]{20}$",
      "maxLength": 24
     },
     "name": {
      "type": "string",
      "maxLength": 63,
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
      "description": "Unique among the organization's templates."
     }
    },
    "additionalProperties": false
   },
   "Snapshot": {
    "type": "object",
    "title": "Snapshot",
    "description": "A saved state of a screen's disk to reset to. It holds the disk only: never the screen's memory and never a secret.",
    "required": [
     "id",
     "screen_id",
     "name",
     "state",
     "size_bytes",
     "created_at"
    ],
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^snap_[0-9a-z]{20}$",
      "maxLength": 25
     },
     "screen_id": {
      "type": "string",
      "pattern": "^scr_[0-9a-z]{20}$",
      "maxLength": 24
     },
     "name": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 63,
        "description": "A label of your own; need not be unique."
       },
       {
        "type": "null"
       }
      ]
     },
     "state": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "creating",
         "ready",
         "failed"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ],
      "description": "creating: being taken."
     },
     "size_bytes": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int64",
        "minimum": 0,
        "maximum": 1125899906842624,
        "description": "Space it holds now: the disk's data changed since it was taken, which grows as the screen writes."
       },
       {
        "type": "null"
       }
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     }
    }
   },
   "SnapshotList": {
    "type": "object",
    "title": "SnapshotList",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Snapshot"
      },
      "maxItems": 3
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "SnapshotCreate": {
    "type": "object",
    "title": "SnapshotCreate",
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 63,
      "description": "Optional label."
     }
    },
    "additionalProperties": false
   },
   "ResetRequest": {
    "type": "object",
    "title": "ResetRequest",
    "required": [
     "snapshot_id"
    ],
    "properties": {
     "snapshot_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^snap_[0-9a-z]{20}$",
        "maxLength": 25,
        "description": "The snapshot to go back to; null: back to how the screen started."
       },
       {
        "type": "null"
       }
      ]
     }
    },
    "additionalProperties": false
   },
   "Operation": {
    "type": "object",
    "title": "Operation",
    "description": "Work that takes longer than one call. Kept 30 days.",
    "required": [
     "id",
     "kind",
     "status",
     "screen_id",
     "created_at",
     "updated_at"
    ],
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^op_[0-9a-z]{20}$",
      "maxLength": 23
     },
     "kind": {
      "$ref": "#/components/schemas/OperationKind"
     },
     "status": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "queued",
         "running",
         "succeeded",
         "failed",
         "reconciling"
        ],
        "maxLength": 12
       },
       {
        "type": "string",
        "maxLength": 12
       }
      ],
      "description": "reconciling: the outcome is uncertain and we are checking the host. Not a failure; do not retry."
     },
     "screen_id": {
      "type": "string",
      "pattern": "^scr_[0-9a-z]{20}$",
      "maxLength": 24
     },
     "template_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^tpl_[0-9a-z]{20}$",
        "maxLength": 24,
        "description": "template.create: the template being saved."
       },
       {
        "type": "null"
       }
      ]
     },
     "snapshot_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^snap_[0-9a-z]{20}$",
        "maxLength": 25,
        "description": "screen.snapshot: the snapshot being taken. screen.reset: the one gone back to; null for a reset to the start."
       },
       {
        "type": "null"
       }
      ]
     },
     "error": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/ErrorBody"
       },
       {
        "type": "null"
       }
      ]
     },
     "progress": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 100,
        "description": "Current step in words, for example `booting`."
       },
       {
        "type": "null"
       }
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "updated_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "done_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "OperationList": {
    "type": "object",
    "title": "OperationList",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Operation"
      },
      "maxItems": 100
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "Screenshot": {
    "type": "object",
    "title": "Screenshot",
    "required": [
     "screenshot_id",
     "mime_type",
     "width",
     "height",
     "screen_width",
     "screen_height",
     "captured_at",
     "input_seq",
     "size_bytes",
     "data"
    ],
    "properties": {
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Names one capture. Pass it to runActions (and to getScreenshot with `region`) so coordinates use its size and stale views are refused."
     },
     "mime_type": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "image/png",
         "image/webp",
         "image/jpeg"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "width": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 9999
     },
     "height": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 9999
     },
     "screen_width": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 9999
     },
     "screen_height": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 9999
     },
     "region": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Region"
       },
       {
        "type": "null"
       }
      ]
     },
     "captured_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "input_seq": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "maximum": 9007199254740992,
      "description": "Input actions run on this screen before the capture. runActions with this screenshot_id refuses to run if it has moved on."
     },
     "settle_ms": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 5000,
        "description": "How long we waited for the screen to go quiet. null: no settle asked."
       },
       {
        "type": "null"
       }
      ]
     },
     "settled": {
      "oneOf": [
       {
        "type": "boolean",
        "description": "false when the settle cap was hit."
       },
       {
        "type": "null"
       }
      ]
     },
     "quality": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 100,
        "description": "webp and jpeg: the quality used after max_bytes."
       },
       {
        "type": "null"
       }
      ]
     },
     "size_bytes": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 40000000,
      "description": "Encoded image size before base64."
     },
     "data": {
      "type": "string",
      "maxLength": 40000000,
      "contentEncoding": "base64",
      "description": "The image, base64. A copy is saved in the screen's history unless detailed history is off."
     },
     "windows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/WindowInfo"
      },
      "maxItems": 50,
      "description": "With `windows=true`: the open windows, topmost first, in this screenshot's pixels (a region's: the full screenshot's). Normal windows and dialogs only; at most 50."
     }
    }
   },
   "WindowRef": {
    "type": "object",
    "title": "WindowRef",
    "description": "A window by its title and app, read from the window manager.",
    "required": [
     "title",
     "app"
    ],
    "properties": {
     "title": {
      "type": "string",
      "maxLength": 200,
      "description": "The window's title, clipped to 200 characters. Read off the screen: history keeps the screenshot, never this text."
     },
     "app": {
      "type": "string",
      "maxLength": 100,
      "description": "The app, from the window's class (for example `chromium`, `org.gnome.TextEditor`)."
     }
    }
   },
   "WindowInfo": {
    "type": "object",
    "title": "WindowInfo",
    "description": "One open window and its place, in the screenshot's pixels.",
    "required": [
     "title",
     "app",
     "x",
     "y",
     "width",
     "height",
     "active"
    ],
    "properties": {
     "title": {
      "type": "string",
      "maxLength": 200,
      "description": "The window's title, clipped to 200 characters. Read off the screen: history keeps the screenshot, never this text."
     },
     "app": {
      "type": "string",
      "maxLength": 100,
      "description": "The app, from the window's class (for example `chromium`, `org.gnome.TextEditor`)."
     },
     "x": {
      "type": "integer",
      "format": "int32",
      "minimum": -9999,
      "maximum": 9999
     },
     "y": {
      "type": "integer",
      "format": "int32",
      "minimum": -9999,
      "maximum": 9999
     },
     "width": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 9999
     },
     "height": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 9999
     },
     "active": {
      "type": "boolean",
      "description": "true for the window with the keyboard focus."
     }
    }
   },
   "ScreenshotOptions": {
    "type": "object",
    "title": "ScreenshotOptions",
    "description": "How the after-shot is taken.",
    "properties": {
     "size": {
      "$ref": "#/components/schemas/ShotSize"
     },
     "format": {
      "$ref": "#/components/schemas/ImageFormat"
     },
     "quality": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 100,
      "default": 80,
      "description": "webp and jpeg only."
     },
     "max_bytes": {
      "$ref": "#/components/schemas/MaxBytes"
     },
     "cursor": {
      "type": "boolean",
      "default": true,
      "description": "Draw the pointer."
     },
     "settle_quiet_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 2000,
      "default": 300,
      "description": "Wait until nothing changed for this long (pointer and caret-sized changes ignored)."
     },
     "settle_max_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 5000,
      "default": 2000,
      "description": "Then capture anyway."
     }
    },
    "additionalProperties": false
   },
   "ActionClick": {
    "type": "object",
    "title": "ActionClick",
    "description": "Click. Without x and y: at the current pointer. count 2 = double, 3 = triple.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "click",
      "maxLength": 20
     },
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "button": {
      "$ref": "#/components/schemas/MouseButton"
     },
     "count": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 3,
      "default": 1
     },
     "modifiers": {
      "$ref": "#/components/schemas/Modifiers"
     }
    },
    "additionalProperties": false
   },
   "ActionMove": {
    "type": "object",
    "title": "ActionMove",
    "description": "Move the pointer (hover).",
    "required": [
     "type",
     "x",
     "y"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "move",
      "maxLength": 20
     },
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     }
    },
    "additionalProperties": false
   },
   "ActionMouseDown": {
    "type": "object",
    "title": "ActionMouseDown",
    "description": "Press a button and hold it. Released by mouse_up, or after 30 s.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "mouse_down",
      "maxLength": 20
     },
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "button": {
      "$ref": "#/components/schemas/MouseButton"
     }
    },
    "additionalProperties": false
   },
   "ActionMouseUp": {
    "type": "object",
    "title": "ActionMouseUp",
    "description": "Release a held button.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "mouse_up",
      "maxLength": 20
     },
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "button": {
      "$ref": "#/components/schemas/MouseButton"
     }
    },
    "additionalProperties": false
   },
   "ActionDrag": {
    "type": "object",
    "title": "ActionDrag",
    "description": "Press at the first point, move through the rest, release at the last.",
    "required": [
     "type",
     "path"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "drag",
      "maxLength": 20
     },
     "path": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Point"
      },
      "maxItems": 100,
      "minItems": 2
     },
     "button": {
      "$ref": "#/components/schemas/MouseButton"
     },
     "modifiers": {
      "$ref": "#/components/schemas/Modifiers"
     }
    },
    "additionalProperties": false
   },
   "ActionScroll": {
    "type": "object",
    "title": "ActionScroll",
    "description": "Scroll at x, y (or the pointer). Positive dy scrolls down, positive dx right. One notch is one wheel click; px converts at 100 px per notch, at least 1.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "scroll",
      "maxLength": 20
     },
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "dx": {
      "type": "integer",
      "format": "int32",
      "minimum": -10000,
      "maximum": 10000,
      "default": 0
     },
     "dy": {
      "type": "integer",
      "format": "int32",
      "minimum": -10000,
      "maximum": 10000,
      "default": 0
     },
     "unit": {
      "type": "string",
      "enum": [
       "notch",
       "px"
      ],
      "default": "notch",
      "maxLength": 5
     },
     "modifiers": {
      "$ref": "#/components/schemas/Modifiers"
     }
    },
    "additionalProperties": false
   },
   "ActionType": {
    "type": "object",
    "title": "ActionType",
    "description": "Type text as keystrokes, or paste it with `via: paste`. Up to 1,000 characters as keys and 100,000 by paste; the result reports characters delivered.",
    "required": [
     "type",
     "text"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "type",
      "maxLength": 20
     },
     "text": {
      "type": "string",
      "maxLength": 100000,
      "minLength": 1
     },
     "enter": {
      "type": "boolean",
      "default": false
     },
     "delay_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100,
      "default": 12
     },
     "via": {
      "type": "string",
      "enum": [
       "keys",
       "paste"
      ],
      "default": "keys",
      "maxLength": 5,
      "description": "keys: keystrokes, up to 1,000 characters. paste: put the text on the clipboard and paste it (ctrl+v, or ctrl+shift+v in a terminal), up to 100,000 characters, for long text and characters the keyboard lacks. It leaves the text on the clipboard."
     }
    },
    "additionalProperties": false
   },
   "ActionKey": {
    "type": "object",
    "title": "ActionKey",
    "description": "Press a key or chord. hold_ms holds it down (Anthropic hold_key).",
    "required": [
     "type",
     "keys"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "key",
      "maxLength": 20
     },
     "keys": {
      "type": "string",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9_+ -]{1,100}$",
      "description": "A key or `+`-joined chord: `enter`, `ctrl+s`, `alt+tab`. xdotool names (`Return`), OpenAI's upper-case names and Playwright names are accepted as aliases."
     },
     "repeat": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 100,
      "default": 1
     },
     "hold_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 20000,
      "default": 0
     }
    },
    "additionalProperties": false
   },
   "ActionKeyDown": {
    "type": "object",
    "title": "ActionKeyDown",
    "description": "Press and hold. Released by key_up, or after 30 s.",
    "required": [
     "type",
     "keys"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "key_down",
      "maxLength": 20
     },
     "keys": {
      "type": "string",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9_+ -]{1,100}$",
      "description": "A key or `+`-joined chord: `enter`, `ctrl+s`, `alt+tab`. xdotool names (`Return`), OpenAI's upper-case names and Playwright names are accepted as aliases."
     }
    },
    "additionalProperties": false
   },
   "ActionKeyUp": {
    "type": "object",
    "title": "ActionKeyUp",
    "description": "Release a held key.",
    "required": [
     "type",
     "keys"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "key_up",
      "maxLength": 20
     },
     "keys": {
      "type": "string",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9_+ -]{1,100}$",
      "description": "A key or `+`-joined chord: `enter`, `ctrl+s`, `alt+tab`. xdotool names (`Return`), OpenAI's upper-case names and Playwright names are accepted as aliases."
     }
    },
    "additionalProperties": false
   },
   "ActionWait": {
    "type": "object",
    "title": "ActionWait",
    "description": "A timed pause inside the batch.",
    "required": [
     "type",
     "ms"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "wait",
      "maxLength": 20
     },
     "ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 20000
     }
    },
    "additionalProperties": false
   },
   "ActionSettle": {
    "type": "object",
    "title": "ActionSettle",
    "description": "Wait until the screen is quiet for quiet_ms (pointer and caret-sized changes ignored), at most max_ms. No image. Use it between a click and the next action, for example after opening a menu.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "settle",
      "maxLength": 20
     },
     "quiet_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 2000,
      "default": 300
     },
     "max_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 5000,
      "default": 2000
     }
    },
    "additionalProperties": false
   },
   "ActionCursorPosition": {
    "type": "object",
    "title": "ActionCursorPosition",
    "description": "Report the pointer position in the coordinate space.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "cursor_position",
      "maxLength": 20
     }
    },
    "additionalProperties": false
   },
   "ActionScreenshot": {
    "type": "object",
    "title": "ActionScreenshot",
    "description": "Capture mid-batch (after settling). Counts toward 4 images per reply. The hosted MCP rejects it inside `act`.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "screenshot",
      "maxLength": 20
     },
     "size": {
      "$ref": "#/components/schemas/ShotSize"
     },
     "format": {
      "$ref": "#/components/schemas/ImageFormat"
     },
     "quality": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 100,
      "default": 80
     },
     "max_bytes": {
      "$ref": "#/components/schemas/MaxBytes"
     },
     "cursor": {
      "type": "boolean",
      "default": true
     }
    },
    "additionalProperties": false
   },
   "ActionZoom": {
    "type": "object",
    "title": "ActionZoom",
    "description": "Capture a region at full resolution. Later points stay in full-screenshot space (Anthropic's rule). The hosted MCP rejects it inside `act`.",
    "required": [
     "type",
     "region"
    ],
    "properties": {
     "type": {
      "type": "string",
      "const": "zoom",
      "maxLength": 20
     },
     "region": {
      "$ref": "#/components/schemas/Region"
     },
     "format": {
      "$ref": "#/components/schemas/ImageFormat"
     },
     "quality": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 100,
      "default": 80
     },
     "max_bytes": {
      "$ref": "#/components/schemas/MaxBytes"
     },
     "max_upscale": {
      "$ref": "#/components/schemas/MaxUpscale"
     }
    },
    "additionalProperties": false
   },
   "Action": {
    "title": "Action",
    "description": "One input step, picked by `type` (15 types). Covers Anthropic's 17 computer tools, OpenAI's computer actions and Gemini's desktop actions; the docs' model guide maps each one.",
    "oneOf": [
     {
      "$ref": "#/components/schemas/ActionClick"
     },
     {
      "$ref": "#/components/schemas/ActionMove"
     },
     {
      "$ref": "#/components/schemas/ActionMouseDown"
     },
     {
      "$ref": "#/components/schemas/ActionMouseUp"
     },
     {
      "$ref": "#/components/schemas/ActionDrag"
     },
     {
      "$ref": "#/components/schemas/ActionScroll"
     },
     {
      "$ref": "#/components/schemas/ActionType"
     },
     {
      "$ref": "#/components/schemas/ActionKey"
     },
     {
      "$ref": "#/components/schemas/ActionKeyDown"
     },
     {
      "$ref": "#/components/schemas/ActionKeyUp"
     },
     {
      "$ref": "#/components/schemas/ActionWait"
     },
     {
      "$ref": "#/components/schemas/ActionSettle"
     },
     {
      "$ref": "#/components/schemas/ActionCursorPosition"
     },
     {
      "$ref": "#/components/schemas/ActionScreenshot"
     },
     {
      "$ref": "#/components/schemas/ActionZoom"
     }
    ],
    "discriminator": {
     "propertyName": "type",
     "mapping": {
      "click": "#/components/schemas/ActionClick",
      "move": "#/components/schemas/ActionMove",
      "mouse_down": "#/components/schemas/ActionMouseDown",
      "mouse_up": "#/components/schemas/ActionMouseUp",
      "drag": "#/components/schemas/ActionDrag",
      "scroll": "#/components/schemas/ActionScroll",
      "type": "#/components/schemas/ActionType",
      "key": "#/components/schemas/ActionKey",
      "key_down": "#/components/schemas/ActionKeyDown",
      "key_up": "#/components/schemas/ActionKeyUp",
      "wait": "#/components/schemas/ActionWait",
      "settle": "#/components/schemas/ActionSettle",
      "cursor_position": "#/components/schemas/ActionCursorPosition",
      "screenshot": "#/components/schemas/ActionScreenshot",
      "zoom": "#/components/schemas/ActionZoom"
     }
    }
   },
   "ActionsRequest": {
    "type": "object",
    "title": "ActionsRequest",
    "required": [
     "actions"
    ],
    "properties": {
     "actions": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Action"
      },
      "maxItems": 20,
      "minItems": 1,
      "description": "Run in order, 100 ms apart. Stops at the first failure; the rest are `not_executed`. The whole batch must fit in 28 s."
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     }
    },
    "additionalProperties": false
   },
   "ActionResult": {
    "type": "object",
    "title": "ActionResult",
    "description": "Fields that do not apply are left out, never null.",
    "required": [
     "index",
     "type",
     "status",
     "duration_ms"
    ],
    "properties": {
     "index": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 19
     },
     "type": {
      "type": "string",
      "maxLength": 20
     },
     "status": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "ok",
         "failed",
         "not_executed"
        ],
        "maxLength": 12
       },
       {
        "type": "string",
        "maxLength": 12
       }
      ]
     },
     "error": {
      "$ref": "#/components/schemas/ErrorBody"
     },
     "cursor": {
      "$ref": "#/components/schemas/Point"
     },
     "typed_chars": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100000
     },
     "screen_changed": {
      "type": "boolean",
      "description": "Input actions only: whether anything visible changed after it (pointer and caret-sized changes ignored), watched until the next action, or up to 500 ms after the last one. false means no visible change, not failure: look again before repeating it."
     },
     "window": {
      "$ref": "#/components/schemas/WindowRef",
      "description": "Input actions only: the topmost window under the point for a pointer action, else the active window that got the keys."
     },
     "screenshot": {
      "$ref": "#/components/schemas/Screenshot"
     },
     "duration_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 30000
     }
    }
   },
   "ActionsResponse": {
    "type": "object",
    "title": "ActionsResponse",
    "required": [
     "status",
     "results",
     "space"
    ],
    "properties": {
     "status": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "completed",
         "partial"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ],
      "description": "partial: an action failed; see results. The after-screenshot is still attached."
     },
     "results": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ActionResult"
      },
      "maxItems": 20
     },
     "screenshot": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Screenshot"
       },
       {
        "type": "null"
       }
      ]
     },
     "screenshot_unchanged": {
      "type": "boolean",
      "description": "if_changed found no visible change."
     },
     "active_window": {
      "$ref": "#/components/schemas/WindowRef",
      "description": "The window with the keyboard focus after the batch. Left out when there is none."
     },
     "windows_opened": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/WindowRef"
      },
      "maxItems": 20,
      "description": "Windows that opened during the batch, newest first. Left out when none did."
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    }
   },
   "ExecCreate": {
    "type": "object",
    "title": "ExecCreate",
    "properties": {
     "command": {
      "type": "string",
      "maxLength": 10000,
      "description": "Shell command, run with `bash -lc` as the desktop user. Send this or argv."
     },
     "argv": {
      "type": "array",
      "items": {
       "type": "string",
       "maxLength": 4000
      },
      "maxItems": 200,
      "minItems": 1,
      "description": "Program and arguments, no shell. Send this or command."
     },
     "cwd": {
      "type": "string",
      "maxLength": 1024,
      "default": "/home/user"
     },
     "env": {
      "type": "object",
      "maxProperties": 50,
      "additionalProperties": {
       "type": "string",
       "maxLength": 4000
      },
      "propertyNames": {
       "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$",
       "maxLength": 64
      }
     },
     "stdin": {
      "type": "string",
      "maxLength": 65536,
      "description": "Written to stdin, then closed."
     },
     "timeout_s": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 86400,
      "default": 3600,
      "description": "0: no limit (dev servers, bots). At the limit we send TERM, then KILL after 10 s; status `timed_out`. Runs on after the call returns."
     },
     "wait_s": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 25,
      "default": 25,
      "description": "Answer inline if done within this; else 202 and an id to poll."
     },
     "max_bytes": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 1048576,
      "default": 65536,
      "description": "Bytes of output per stream in this reply."
     },
     "tail": {
      "type": "boolean",
      "default": false,
      "description": "true: the last max_bytes after the offset (errors and exit lines). false: the first."
     }
    },
    "additionalProperties": false
   },
   "Exec": {
    "type": "object",
    "title": "Exec",
    "required": [
     "id",
     "screen_id",
     "status",
     "started_at",
     "stdout",
     "stderr",
     "stdout_bytes",
     "stderr_bytes",
     "incarnation"
    ],
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^ex_[0-9a-z]{20}$",
      "maxLength": 23
     },
     "screen_id": {
      "type": "string",
      "pattern": "^scr_[0-9a-z]{20}$",
      "maxLength": 24
     },
     "status": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "running",
         "exited",
         "killed",
         "timed_out",
         "lost"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ],
      "description": "lost: the screen restarted mid-run; the exit status is unknown. Never rerun blindly."
     },
     "exit_code": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 255
       },
       {
        "type": "null"
       }
      ]
     },
     "signal": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 10
       },
       {
        "type": "null"
       }
      ]
     },
     "started_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "ended_at": {
      "oneOf": [
       {
        "type": "string",
        "format": "date-time",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     },
     "duration_ms": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int64",
        "minimum": 0,
        "maximum": 1099511627776
       },
       {
        "type": "null"
       }
      ]
     },
     "stdout": {
      "type": "string",
      "maxLength": 1048576,
      "description": "Output from `stdout_from`, up to max_bytes (head or tail). UTF-8, invalid bytes replaced."
     },
     "stderr": {
      "type": "string",
      "maxLength": 1048576
     },
     "stdout_bytes": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "maximum": 1099511627776,
      "description": "Total bytes written so far. Use as the next stdout_from."
     },
     "stderr_bytes": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "maximum": 1099511627776
     },
     "output_dropped": {
      "type": "boolean",
      "description": "Older output fell out of the 1 MiB per-stream buffer."
     },
     "incarnation": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 2147483647
     }
    }
   },
   "ExecKill": {
    "type": "object",
    "title": "ExecKill",
    "properties": {
     "signal": {
      "type": "string",
      "enum": [
       "TERM",
       "INT",
       "KILL"
      ],
      "default": "TERM",
      "maxLength": 4
     }
    },
    "additionalProperties": false
   },
   "FileWritten": {
    "type": "object",
    "title": "FileWritten",
    "required": [
     "path",
     "size_bytes",
     "sha256"
    ],
    "properties": {
     "path": {
      "type": "string",
      "maxLength": 1024
     },
     "size_bytes": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 67108864
     },
     "sha256": {
      "type": "string",
      "maxLength": 64,
      "pattern": "^[0-9a-f]{64}$"
     }
    }
   },
   "FileEntry": {
    "type": "object",
    "title": "FileEntry",
    "required": [
     "name",
     "type",
     "size_bytes",
     "modified_at"
    ],
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 255
     },
     "type": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "file",
         "dir",
         "symlink",
         "other"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "size_bytes": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "maximum": 1125899906842624
     },
     "modified_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     }
    }
   },
   "Clipboard": {
    "type": "object",
    "title": "Clipboard",
    "required": [
     "text",
     "truncated"
    ],
    "properties": {
     "text": {
      "type": "string",
      "maxLength": 100000,
      "description": "The clipboard's text; empty when it holds none or holds no text."
     },
     "truncated": {
      "type": "boolean",
      "description": "true when the clipboard held more than 100,000 characters and only the first came back."
     }
    }
   },
   "ClipboardSet": {
    "type": "object",
    "title": "ClipboardSet",
    "required": [
     "text"
    ],
    "properties": {
     "text": {
      "type": "string",
      "maxLength": 100000,
      "description": "Text to put on the clipboard; empty clears it."
     }
    },
    "additionalProperties": false
   },
   "FileList": {
    "type": "object",
    "title": "FileList",
    "description": "Entries sorted by name. Folders first.",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/FileEntry"
      },
      "maxItems": 1000
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "ViewerCreate": {
    "type": "object",
    "title": "ViewerCreate",
    "properties": {
     "mode": {
      "type": "string",
      "enum": [
       "view",
       "interactive"
      ],
      "default": "view",
      "maxLength": 12,
      "description": "interactive needs a person (cookie). Agents get view links."
     }
    },
    "additionalProperties": false
   },
   "Viewer": {
    "type": "object",
    "title": "Viewer",
    "description": "`expires_at`: open the link by then. `session_expires_at`: the live session ends then (60 minutes for agent-minted links). Revoking the credential that minted it closes it.",
    "required": [
     "url",
     "mode",
     "expires_at",
     "session_expires_at"
    ],
    "properties": {
     "url": {
      "type": "string",
      "maxLength": 500,
      "format": "uri",
      "description": "Open within 60 s. Single use. The ticket rides in the URL fragment."
     },
     "mode": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "view",
         "interactive"
        ],
        "maxLength": 12
       },
       {
        "type": "string",
        "maxLength": 12
       }
      ]
     },
     "expires_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "session_expires_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     }
    }
   },
   "Actor": {
    "type": "object",
    "title": "Actor",
    "description": "Who made the call: an API key, a connected app, a signed-in person, or Kubi itself.",
    "required": [
     "type"
    ],
    "properties": {
     "type": {
      "anyOf": [
       {
        "type": "string",
        "enum": [
         "api_key",
         "app",
         "person",
         "system"
        ],
        "maxLength": 10
       },
       {
        "type": "string",
        "maxLength": 10
       }
      ]
     },
     "credential_id": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     },
     "credential_name": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 100
       },
       {
        "type": "null"
       }
      ]
     },
     "user_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^usr_[0-9a-z]{20}$",
        "maxLength": 24
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "HistoryEntry": {
    "type": "object",
    "title": "HistoryEntry",
    "description": "One call or event. Kept 30 days.",
    "required": [
     "id",
     "at",
     "kind",
     "actor",
     "summary",
     "result",
     "screenshot_saved"
    ],
    "properties": {
     "id": {
      "type": "string",
      "maxLength": 30,
      "pattern": "^evt_[0-9a-z]{20}$"
     },
     "at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     },
     "screen_id": {
      "oneOf": [
       {
        "type": "string",
        "pattern": "^scr_[0-9a-z]{20}$",
        "maxLength": 24
       },
       {
        "type": "null"
       }
      ]
     },
     "screen_kind": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/ScreenKind",
        "description": "The screen's kind when the row was written; null for an account event."
       },
       {
        "type": "null"
       }
      ]
     },
     "kind": {
      "type": "string",
      "maxLength": 40,
      "description": "`call` for a call on the screen; otherwise an event such as screen.created, screen.deleted, key.created, key.revoked, app.connected, secret.set, proxy.set, control.taken or control.released. New kinds may appear."
     },
     "operation": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 40,
        "description": "For `call`: the operation called, such as `runActions`."
       },
       {
        "type": "null"
       }
      ]
     },
     "actor": {
      "$ref": "#/components/schemas/Actor"
     },
     "run": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 100,
        "description": "The `Kubi-Run` label the call carried."
       },
       {
        "type": "null"
       }
      ]
     },
     "summary": {
      "type": "string",
      "maxLength": 300,
      "description": "For example `click (450, 280); type 15 chars; key enter`."
     },
     "params": {
      "oneOf": [
       {
        "type": "object",
        "maxProperties": 50,
        "additionalProperties": true,
        "description": "The call's parameters, typed text included; null when detailed history is off. Text that matches an organization secret is replaced by the secret's name in brackets."
       },
       {
        "type": "null"
       }
      ]
     },
     "result": {
      "type": "string",
      "maxLength": 63,
      "description": "`ok` or an error code."
     },
     "duration_ms": {
      "oneOf": [
       {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 2147483647
       },
       {
        "type": "null"
       }
      ]
     },
     "screenshot_id": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 25,
        "pattern": "^shot_[0-9a-z]{20}$",
        "description": "The screenshot the call returned."
       },
       {
        "type": "null"
       }
      ]
     },
     "screenshot_saved": {
      "type": "boolean",
      "description": "true when getSavedScreenshot returns the image. false when detailed history is off or the upload failed."
     },
     "request_id": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 40
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "HistoryList": {
    "type": "object",
    "title": "HistoryList",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/HistoryEntry"
      },
      "maxItems": 100
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "Me": {
    "type": "object",
    "title": "Me",
    "description": "Who is calling and what it may do. Agents call this first.",
    "required": [
     "organization",
     "credential",
     "screens",
     "templates",
     "billing",
     "limits"
    ],
    "properties": {
     "organization": {
      "type": "object",
      "required": [
       "id",
       "name"
      ],
      "properties": {
       "id": {
        "type": "string",
        "pattern": "^org_[0-9a-z]{20}$",
        "maxLength": 24
       },
       "name": {
        "type": "string",
        "maxLength": 100
       }
      }
     },
     "user": {
      "oneOf": [
       {
        "type": "object",
        "required": [
         "id",
         "name"
        ],
        "properties": {
         "id": {
          "type": "string",
          "pattern": "^usr_[0-9a-z]{20}$",
          "maxLength": 24
         },
         "name": {
          "type": "string",
          "maxLength": 100
         },
         "email": {
          "type": "string",
          "maxLength": 254,
          "format": "email"
         }
        }
       },
       {
        "type": "null"
       }
      ]
     },
     "credential": {
      "type": "object",
      "required": [
       "type",
       "permissions"
      ],
      "properties": {
       "type": {
        "anyOf": [
         {
          "type": "string",
          "enum": [
           "cookie",
           "api_key",
           "oauth"
          ],
          "maxLength": 10
         },
         {
          "type": "string",
          "maxLength": 10
         }
        ]
       },
       "id": {
        "oneOf": [
         {
          "type": "string",
          "maxLength": 40
         },
         {
          "type": "null"
         }
        ]
       },
       "name": {
        "oneOf": [
         {
          "type": "string",
          "maxLength": 100
         },
         {
          "type": "null"
         }
        ]
       },
       "permissions": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/Permission"
        },
        "maxItems": 4
       },
       "screen_ids": {
        "oneOf": [
         {
          "type": "array",
          "items": {
           "type": "string",
           "pattern": "^scr_[0-9a-z]{20}$",
           "maxLength": 24
          },
          "maxItems": 100,
          "description": "null: every screen in the organization."
         },
         {
          "type": "null"
         }
        ]
       },
       "expires_at": {
        "oneOf": [
         {
          "type": "string",
          "format": "date-time",
          "maxLength": 40
         },
         {
          "type": "null"
         }
        ]
       }
      }
     },
     "screens": {
      "type": "object",
      "required": [
       "count",
       "cap"
      ],
      "properties": {
       "count": {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 1000,
        "description": "Screens in the organization, running or stopped; each is billed."
       },
       "cap": {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 1000,
        "description": "The organization's screen cap; createScreen over it gets 402 QUOTA_EXCEEDED."
       }
      }
     },
     "templates": {
      "type": "object",
      "required": [
       "count",
       "cap"
      ],
      "properties": {
       "count": {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 1000
       },
       "cap": {
        "type": "integer",
        "format": "int32",
        "minimum": 0,
        "maximum": 1000,
        "description": "createTemplate over it gets 402 QUOTA_EXCEEDED."
       }
      }
     },
     "billing": {
      "type": "object",
      "required": [
       "state",
       "credit_state",
       "manage_url"
      ],
      "properties": {
       "state": {
        "anyOf": [
         {
          "type": "string",
          "enum": [
           "none",
           "ok",
           "grace",
           "hold",
           "canceled",
           "closing",
           "closed"
          ],
          "maxLength": 10
         },
         {
          "type": "string",
          "maxLength": 10
         }
        ],
        "description": "The organization's money state. grace: a payment failed and the card is being retried; screens keep running. hold: billing stopped the screens until a person pays."
       },
       "credit_state": {
        "anyOf": [
         {
          "type": "string",
          "enum": [
           "ok",
           "low",
           "empty"
          ],
          "maxLength": 6
         },
         {
          "type": "string",
          "maxLength": 6
         }
        ],
        "description": "Usage credit for disposable screens, without the amount: low is under the organization's warning mark; empty refuses disposable creates with CREDIT_EXHAUSTED."
       },
       "warning": {
        "oneOf": [
         {
          "type": "string",
          "maxLength": 300
         },
         {
          "type": "null"
         }
        ]
       },
       "manage_url": {
        "type": "string",
        "maxLength": 300,
        "format": "uri"
       }
      }
     },
     "limits": {
      "type": "object",
      "required": [
       "actions_per_call",
       "images_per_reply",
       "call_time_limit_s",
       "file_max_bytes",
       "requests_per_minute",
       "image_calls_per_second",
       "calls_per_second"
      ],
      "properties": {
       "actions_per_call": {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 100
       },
       "images_per_reply": {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 10
       },
       "call_time_limit_s": {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 60
       },
       "file_max_bytes": {
        "type": "integer",
        "format": "int64",
        "minimum": 0,
        "maximum": 1099511627776
       },
       "requests_per_minute": {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 100000,
        "description": "Per credential: 60 per screen in the organization, at least 300."
       },
       "image_calls_per_second": {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 100,
        "description": "Per screen: screenshots and after-shots."
       },
       "calls_per_second": {
        "type": "integer",
        "format": "int32",
        "minimum": 1,
        "maximum": 1000,
        "description": "Per screen: all other calls."
       }
      }
     }
    }
   },
   "Secret": {
    "type": "object",
    "title": "Secret",
    "description": "Name and screens only: the value is never shown.",
    "required": [
     "name",
     "updated_at"
    ],
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 64
     },
     "screen_ids": {
      "oneOf": [
       {
        "type": "array",
        "items": {
         "type": "string",
         "pattern": "^scr_[0-9a-z]{20}$",
         "maxLength": 24
        },
        "maxItems": 100
       },
       {
        "type": "null"
       }
      ]
     },
     "updated_at": {
      "type": "string",
      "format": "date-time",
      "maxLength": 40
     }
    }
   },
   "SecretList": {
    "type": "object",
    "title": "SecretList",
    "required": [
     "data",
     "next"
    ],
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Secret"
      },
      "maxItems": 100
     },
     "next": {
      "oneOf": [
       {
        "type": "string",
        "maxLength": 200
       },
       {
        "type": "null"
       }
      ]
     }
    }
   },
   "ClickRequest": {
    "type": "object",
    "title": "ClickRequest",
    "description": "One `click` action plus the step options a batch takes.",
    "properties": {
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "button": {
      "$ref": "#/components/schemas/MouseButton"
     },
     "count": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 3,
      "default": 1
     },
     "modifiers": {
      "$ref": "#/components/schemas/Modifiers"
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    },
    "additionalProperties": false
   },
   "TypeRequest": {
    "type": "object",
    "title": "TypeRequest",
    "description": "One `type` action plus the step options a batch takes.",
    "required": [
     "text"
    ],
    "properties": {
     "text": {
      "type": "string",
      "maxLength": 100000,
      "minLength": 1
     },
     "enter": {
      "type": "boolean",
      "default": false
     },
     "delay_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 100,
      "default": 12
     },
     "via": {
      "type": "string",
      "enum": [
       "keys",
       "paste"
      ],
      "default": "keys",
      "maxLength": 5,
      "description": "keys: keystrokes, up to 1,000 characters. paste: put the text on the clipboard and paste it (ctrl+v, or ctrl+shift+v in a terminal), up to 100,000 characters, for long text and characters the keyboard lacks. It leaves the text on the clipboard."
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    },
    "additionalProperties": false
   },
   "PressRequest": {
    "type": "object",
    "title": "PressRequest",
    "description": "One `key` action plus the step options a batch takes.",
    "required": [
     "keys"
    ],
    "properties": {
     "keys": {
      "type": "string",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9_+ -]{1,100}$",
      "description": "A key or `+`-joined chord: `enter`, `ctrl+s`, `alt+tab`. xdotool names (`Return`), OpenAI's upper-case names and Playwright names are accepted as aliases."
     },
     "repeat": {
      "type": "integer",
      "format": "int32",
      "minimum": 1,
      "maximum": 100,
      "default": 1
     },
     "hold_ms": {
      "type": "integer",
      "format": "int32",
      "minimum": 0,
      "maximum": 20000,
      "default": 0
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    },
    "additionalProperties": false
   },
   "ScrollRequest": {
    "type": "object",
    "title": "ScrollRequest",
    "description": "One `scroll` action plus the step options a batch takes.",
    "properties": {
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "dx": {
      "type": "integer",
      "format": "int32",
      "minimum": -10000,
      "maximum": 10000,
      "default": 0
     },
     "dy": {
      "type": "integer",
      "format": "int32",
      "minimum": -10000,
      "maximum": 10000,
      "default": 0
     },
     "unit": {
      "type": "string",
      "enum": [
       "notch",
       "px"
      ],
      "default": "notch",
      "maxLength": 5
     },
     "modifiers": {
      "$ref": "#/components/schemas/Modifiers"
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    },
    "additionalProperties": false
   },
   "DragRequest": {
    "type": "object",
    "title": "DragRequest",
    "description": "One `drag` action plus the step options a batch takes.",
    "required": [
     "path"
    ],
    "properties": {
     "path": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Point"
      },
      "maxItems": 100,
      "minItems": 2
     },
     "button": {
      "$ref": "#/components/schemas/MouseButton"
     },
     "modifiers": {
      "$ref": "#/components/schemas/Modifiers"
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    },
    "additionalProperties": false
   },
   "MoveRequest": {
    "type": "object",
    "title": "MoveRequest",
    "description": "One `move` action plus the step options a batch takes.",
    "required": [
     "x",
     "y"
    ],
    "properties": {
     "x": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "y": {
      "$ref": "#/components/schemas/Coordinate"
     },
     "screenshot_id": {
      "type": "string",
      "maxLength": 25,
      "pattern": "^shot_[0-9a-z]{20}$",
      "description": "Optional precondition and coordinate default: the screenshot you acted on. If any input ran after it (a person's in the live view included) or the screen restarted, nothing runs: 409 STALE_OBSERVATION. The hosted MCP always sends it."
     },
     "screenshot": {
      "type": "string",
      "enum": [
       "after",
       "if_changed",
       "none"
      ],
      "default": "none",
      "maxLength": 10,
      "description": "after: attach the screen after the last action that ran, once it settles. if_changed: only if it changed visibly since `screenshot_id` (else `screenshot_unchanged: true`). none: no image. The hosted MCP sends `after`."
     },
     "screenshot_options": {
      "$ref": "#/components/schemas/ScreenshotOptions"
     },
     "space": {
      "$ref": "#/components/schemas/Space"
     }
    },
    "additionalProperties": false
   }
  },
  "parameters": {
   "IdempotencyKey": {
    "name": "Idempotency-Key",
    "in": "header",
    "required": false,
    "description": "Any unique string, ideally a UUIDv7; the SDK and MCP always send one. A retry with the same key and body gets the stored answer instead of running twice (kept 24 h for screens and power, 10 minutes for actions and commands). Stored once execution began: 2xx, 500 and 504. Never stored when `outcome` is `not_applied` (400, 401, 403, 404, 409, 415, 422, 429), so the same key can be retried after a conflict or a rate limit. Replays of runActions keep per-action results and shot metadata only, capture a fresh screenshot and carry `Idempotent-Replayed: true`. Same key, different body: 422 IDEMPOTENCY_KEY_REUSED. Same key still running: 409 IDEMPOTENCY_KEY_IN_USE.",
    "schema": {
     "type": "string",
     "maxLength": 255,
     "minLength": 8,
     "pattern": "^[ -~]+$"
    },
    "example": "01929f6e-7c1a-7b3e-9d2f-5a8c4e6b1d03"
   },
   "Limit": {
    "name": "limit",
    "in": "query",
    "required": false,
    "description": "Page size, 1 to 100.",
    "schema": {
     "type": "integer",
     "format": "int32",
     "minimum": 1,
     "maximum": 100,
     "default": 20
    }
   },
   "After": {
    "name": "after",
    "in": "query",
    "required": false,
    "description": "Opaque cursor from the previous page's `next`.",
    "schema": {
     "type": "string",
     "maxLength": 200,
     "pattern": "^[A-Za-z0-9_-]+$"
    }
   },
   "WaitS": {
    "name": "wait_s",
    "in": "query",
    "required": false,
    "description": "Long poll: hold the reply up to this many seconds until something changes. 0 answers at once. Capped at 25 so every call ends within 30 s.",
    "schema": {
     "type": "integer",
     "format": "int32",
     "minimum": 0,
     "maximum": 25,
     "default": 0
    }
   },
   "ScreenId": {
    "name": "screen_id",
    "in": "path",
    "required": true,
    "description": "Screen id (`scr_...`).",
    "schema": {
     "type": "string",
     "pattern": "^scr_[0-9a-z]{20}$",
     "maxLength": 24
    },
    "example": "scr_7f2kq9w3x8m4b6tzp1ac"
   },
   "OperationId": {
    "name": "operation_id",
    "in": "path",
    "required": true,
    "description": "Operation id (`op_...`).",
    "schema": {
     "type": "string",
     "pattern": "^op_[0-9a-z]{20}$",
     "maxLength": 23
    },
    "example": "op_01jb7q0x9v2k8m4n6p3r"
   },
   "TemplateId": {
    "name": "template_id",
    "in": "path",
    "required": true,
    "description": "Template id (`tpl_...`).",
    "schema": {
     "type": "string",
     "pattern": "^tpl_[0-9a-z]{20}$",
     "maxLength": 24
    },
    "example": "tpl_01jb9a2m5k8q3w9x4z6t"
   },
   "SnapshotId": {
    "name": "snapshot_id",
    "in": "path",
    "required": true,
    "description": "Snapshot id (`snap_...`).",
    "schema": {
     "type": "string",
     "pattern": "^snap_[0-9a-z]{20}$",
     "maxLength": 25
    },
    "example": "snap_01jb8a2m5k8q3w9x4z6t"
   },
   "ExecId": {
    "name": "exec_id",
    "in": "path",
    "required": true,
    "description": "Exec id (`ex_...`) from startExec.",
    "schema": {
     "type": "string",
     "pattern": "^ex_[0-9a-z]{20}$",
     "maxLength": 23
    },
    "example": "ex_01jb7r2m5k8q3w9x4z6t"
   },
   "FilePath": {
    "name": "path",
    "in": "query",
    "required": true,
    "description": "Absolute path on the screen, or relative to /home/user.",
    "schema": {
     "type": "string",
     "maxLength": 1024,
     "minLength": 1
    },
    "example": "/home/user/report.pdf"
   },
   "ScreenshotId": {
    "name": "screenshot_id",
    "in": "path",
    "required": true,
    "description": "Saved screenshot id (`shot_...`), from a reply or a history row.",
    "schema": {
     "type": "string",
     "maxLength": 25,
     "pattern": "^shot_[0-9a-z]{20}$"
    },
    "example": "shot_01jb7q2x9k4m8q1w3z5t"
   },
   "Run": {
    "name": "Kubi-Run",
    "in": "header",
    "required": false,
    "description": "Optional label that groups calls into one run in the screen's history. It is only a label and never locks the screen. The SDK fills it from KUBI_RUN, else Claude Code's CLAUDE_CODE_SESSION_ID, else one id per client it creates.",
    "schema": {
     "type": "string",
     "maxLength": 100,
     "pattern": "^[ -~]{1,100}$"
    },
    "example": "6f1c2a90-3d4b-4e8f-9a1c-7b2d5e8f0a13"
   }
  },
  "headers": {
   "RequestId": {
    "description": "Id of this request (also in error bodies and the screen's history).",
    "schema": {
     "type": "string",
     "maxLength": 40,
     "pattern": "^req_[0-9a-z]{20}$"
    }
   },
   "RateLimit": {
    "description": "Informative: remaining requests and seconds to reset (IETF draft). Retry-After on 429 is the contract.",
    "schema": {
     "type": "string",
     "maxLength": 100
    },
    "example": "\"default\";r=287;t=41"
   },
   "RateLimitPolicy": {
    "description": "Informative: the quota and window that apply. Per credential: 60 a minute per screen in the organization, at least 300. Per screen: 2 image calls and 10 other calls a second.",
    "schema": {
     "type": "string",
     "maxLength": 100
    },
    "example": "\"default\";q=300;w=60"
   },
   "RetryAfter": {
    "description": "Seconds to wait before retrying or polling.",
    "schema": {
     "type": "integer",
     "format": "int32",
     "minimum": 0,
     "maximum": 86400
    }
   },
   "IdempotentReplayed": {
    "description": "true when this answer replays an earlier request with the same key.",
    "schema": {
     "type": "string",
     "enum": [
      "true"
     ],
     "maxLength": 4
    }
   },
   "BillingWarning": {
    "description": "Present while a payment is late. Pass it on to the person.",
    "schema": {
     "type": "string",
     "maxLength": 300
    },
    "example": "Payment failed. Screens stop 29 Oct. Pay: https://console.trykubi.com/settings/billing"
   },
   "Deprecation": {
    "description": "RFC 9745: present when this route is deprecated, with the date it was. The SDK logs one warning.",
    "schema": {
     "type": "string",
     "maxLength": 30,
     "pattern": "^@[0-9]{1,12}$"
    },
    "example": "@1798761600"
   },
   "Sunset": {
    "description": "RFC 8594: present when the route has an end date (HTTP date).",
    "schema": {
     "type": "string",
     "maxLength": 40
    },
    "example": "Mon, 01 Nov 2027 00:00:00 GMT"
   },
   "WWWAuthenticate": {
    "description": "401: where to find the authorization server (RFC 9728 metadata). 403 PERMISSION_DENIED: the missing scope.",
    "schema": {
     "type": "string",
     "maxLength": 300
    },
    "example": "Bearer resource_metadata=\"https://api.trykubi.com/.well-known/oauth-protected-resource/v1\""
   },
   "Location": {
    "description": "URL of the operation to poll.",
    "schema": {
     "type": "string",
     "maxLength": 300,
     "format": "uri-reference"
    }
   }
  },
  "responses": {
   "BadRequest": {
    "description": "Bad request. Codes: INVALID_ARGUMENT, COORDINATE_OUT_OF_RANGE, BATCH_TOO_LONG.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "invalid_argument": {
        "summary": "INVALID_ARGUMENT",
        "value": {
         "error": {
          "code": "INVALID_ARGUMENT",
          "message": "A field is missing, malformed or out of range (details.field, details.problem). Fix that field and resend.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#invalid_argument",
          "details": {
           "field": "actions[0].y",
           "problem": "required"
          }
         }
        }
       },
       "coordinate_out_of_range": {
        "summary": "COORDINATE_OUT_OF_RANGE",
        "value": {
         "error": {
          "code": "COORDINATE_OUT_OF_RANGE",
          "message": "A point lies outside the coordinate space; it is rejected, never clamped. Use a point inside details.space.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#coordinate_out_of_range",
          "details": {
           "action_index": 0,
           "space": [
            1280,
            720
           ],
           "x": 1400,
           "y": 280
          }
         }
        }
       },
       "batch_too_long": {
        "summary": "BATCH_TOO_LONG",
        "value": {
         "error": {
          "code": "BATCH_TOO_LONG",
          "message": "The batch would run past 28 s, inside the 30 s call limit. Split it into smaller batches.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#batch_too_long"
         }
        }
       }
      }
     }
    }
   },
   "Unauthorized": {
    "description": "Not authenticated. Codes: UNAUTHENTICATED.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     },
     "WWW-Authenticate": {
      "$ref": "#/components/headers/WWWAuthenticate"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "unauthenticated": {
        "summary": "UNAUTHENTICATED",
        "value": {
         "error": {
          "code": "UNAUTHENTICATED",
          "message": "The credential is missing, malformed, unknown, revoked or expired (details.reason). Send a valid key or token.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#unauthenticated",
          "details": {
           "reason": "expired"
          }
         }
        }
       }
      }
     }
    }
   },
   "PaymentRequired": {
    "description": "Payment needed. Codes: QUOTA_EXCEEDED, PAYMENT_REQUIRED, CREDIT_EXHAUSTED, SUBSCRIPTION_REQUIRED.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "quota_exceeded": {
        "summary": "QUOTA_EXCEEDED",
        "value": {
         "error": {
          "code": "QUOTA_EXCEEDED",
          "message": "The organization is at its screen cap (details.screen_cap). Delete a screen, or a person asks support for a higher cap.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#quota_exceeded",
          "details": {
           "screens": 3,
           "screen_cap": 3,
           "billing_url": "https://console.trykubi.com/settings/billing"
          }
         }
        }
       },
       "payment_required": {
        "summary": "PAYMENT_REQUIRED",
        "value": {
         "error": {
          "code": "PAYMENT_REQUIRED",
          "message": "Payment failed. A person pays at details.pay_url.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#payment_required",
          "details": {
           "pay_url": "https://invoice.stripe.com/i/acct_test/inv_test",
           "stops_at": "2026-10-29T00:00:00Z"
          }
         }
        }
       },
       "credit_exhausted": {
        "summary": "CREDIT_EXHAUSTED",
        "value": {
         "error": {
          "code": "CREDIT_EXHAUSTED",
          "message": "The usage credit that pays for disposable screens is used up (details.balance_usd). A person tops up at details.billing_url, or create a persistent screen.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#credit_exhausted",
          "details": {
           "balance_usd": 0,
           "billing_url": "https://console.trykubi.com/settings/billing"
          }
         }
        }
       },
       "subscription_required": {
        "summary": "SUBSCRIPTION_REQUIRED",
        "value": {
         "error": {
          "code": "SUBSCRIPTION_REQUIRED",
          "message": "Buying usage credit needs a paid persistent screen first. A person subscribes at details.billing_url.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#subscription_required",
          "details": {
           "billing_url": "https://console.trykubi.com/settings/billing"
          }
         }
        }
       }
      }
     }
    }
   },
   "Forbidden": {
    "description": "Not allowed. Codes: PERMISSION_DENIED, HUMAN_ONLY, CSRF_REJECTED, ORGANIZATION_SUSPENDED, ACCOUNT_PENDING.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     },
     "WWW-Authenticate": {
      "description": "Sent with PERMISSION_DENIED: `Bearer error=\"insufficient_scope\", scope=\"exec\"`.",
      "schema": {
       "type": "string",
       "maxLength": 300
      }
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "permission_denied": {
        "summary": "PERMISSION_DENIED",
        "value": {
         "error": {
          "code": "PERMISSION_DENIED",
          "message": "The credential lacks details.needed, or is not bound to this screen. Use a credential that has it.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#permission_denied",
          "details": {
           "needed": "exec",
           "has": [
            "read",
            "control"
           ]
          }
         }
        }
       },
       "human_only": {
        "summary": "HUMAN_ONLY",
        "value": {
         "error": {
          "code": "HUMAN_ONLY",
          "message": "Only a signed-in person (dashboard cookie) may call this. Ask a person to do it in the dashboard.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#human_only"
         }
        }
       },
       "csrf_rejected": {
        "summary": "CSRF_REJECTED",
        "value": {
         "error": {
          "code": "CSRF_REJECTED",
          "message": "A cookie request changed state without the dashboard's Origin. Send it from the dashboard, or use an API key.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#csrf_rejected"
         }
        }
       },
       "organization_suspended": {
        "summary": "ORGANIZATION_SUSPENDED",
        "value": {
         "error": {
          "code": "ORGANIZATION_SUSPENDED",
          "message": "The organization is suspended. Stop and tell the user.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#organization_suspended"
         }
        }
       },
       "account_pending": {
        "summary": "ACCOUNT_PENDING",
        "value": {
         "error": {
          "code": "ACCOUNT_PENDING",
          "message": "The account is waiting for approval. Stop and tell the user.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#account_pending"
         }
        }
       }
      }
     }
    }
   },
   "NotFound": {
    "description": "Not found. Codes: SCREEN_NOT_FOUND, NOT_FOUND, FILE_NOT_FOUND.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "screen_not_found": {
        "summary": "SCREEN_NOT_FOUND",
        "value": {
         "error": {
          "code": "SCREEN_NOT_FOUND",
          "message": "No such screen in your organization (the same answer for another organization's id). List screens for a valid id.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#screen_not_found"
         }
        }
       },
       "not_found": {
        "summary": "NOT_FOUND",
        "value": {
         "error": {
          "code": "NOT_FOUND",
          "message": "No such operation, exec, key, app, export, template, snapshot or saved screenshot (saved ones go after 30 days). Check the id.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#not_found"
         }
        }
       },
       "file_not_found": {
        "summary": "FILE_NOT_FOUND",
        "value": {
         "error": {
          "code": "FILE_NOT_FOUND",
          "message": "Nothing at that path on the screen. List the folder with listFiles.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#file_not_found"
         }
        }
       }
      }
     }
    }
   },
   "Conflict": {
    "description": "Conflict with the screen's state. Codes: NO_CAPACITY, STALE_OBSERVATION, CONTROL_HELD, SCREEN_NOT_RUNNING, OPERATION_IN_PROGRESS, IDEMPOTENCY_KEY_IN_USE, UNSUPPORTED_CAPABILITY.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "no_capacity": {
        "summary": "NO_CAPACITY",
        "value": {
         "error": {
          "code": "NO_CAPACITY",
          "message": "No free machine for a new screen right now; nothing was charged. Retry after retry_after.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": true,
          "retry_after": 60,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#no_capacity"
         }
        }
       },
       "stale_observation": {
        "summary": "STALE_OBSERVATION",
        "value": {
         "error": {
          "code": "STALE_OBSERVATION",
          "message": "Input ran (anyone's, a person's in the live view included) or the screen restarted since your screenshot (details.acted_since, details.restarted). Take a new screenshot first.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#stale_observation",
          "details": {
           "acted_since": 2,
           "restarted": false
          }
         }
        }
       },
       "control_held": {
        "summary": "CONTROL_HELD",
        "value": {
         "error": {
          "code": "CONTROL_HELD",
          "message": "A person has control of this screen, so nothing ran. Control ends when they release it, or after 5 minutes with no input from them. Wait retry_after seconds, then take a new screenshot before acting.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": 300,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#control_held"
         }
        }
       },
       "screen_not_running": {
        "summary": "SCREEN_NOT_RUNNING",
        "value": {
         "error": {
          "code": "SCREEN_NOT_RUNNING",
          "message": "The screen is stopped, starting or stopping. Start it, or wait on its operation.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#screen_not_running"
         }
        }
       },
       "operation_in_progress": {
        "summary": "OPERATION_IN_PROGRESS",
        "value": {
         "error": {
          "code": "OPERATION_IN_PROGRESS",
          "message": "Another power operation runs on this screen. Wait on it, then retry.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": true,
          "retry_after": 5,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#operation_in_progress"
         }
        }
       },
       "idempotency_key_in_use": {
        "summary": "IDEMPOTENCY_KEY_IN_USE",
        "value": {
         "error": {
          "code": "IDEMPOTENCY_KEY_IN_USE",
          "message": "A request with this key is still running. Retry the same request.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": true,
          "retry_after": 1,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#idempotency_key_in_use"
         }
        }
       },
       "unsupported_capability": {
        "summary": "UNSUPPORTED_CAPABILITY",
        "value": {
         "error": {
          "code": "UNSUPPORTED_CAPABILITY",
          "message": "This screen cannot do that (for example a screenless box). Use a screen that can.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#unsupported_capability"
         }
        }
       }
      }
     }
    }
   },
   "PayloadTooLarge": {
    "description": "Too large. Codes: PAYLOAD_TOO_LARGE.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "payload_too_large": {
        "summary": "PAYLOAD_TOO_LARGE",
        "value": {
         "error": {
          "code": "PAYLOAD_TOO_LARGE",
          "message": "The body or file is over details.limit_bytes. Send less, or move big files through exec.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#payload_too_large",
          "details": {
           "limit_bytes": 67108864
          }
         }
        }
       }
      }
     }
    }
   },
   "UnsupportedMediaType": {
    "description": "Unsupported media type. Codes: UNSUPPORTED_MEDIA_TYPE.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "unsupported_media_type": {
        "summary": "UNSUPPORTED_MEDIA_TYPE",
        "value": {
         "error": {
          "code": "UNSUPPORTED_MEDIA_TYPE",
          "message": "Wrong Content-Type. Send application/json (application/octet-stream for file uploads).",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#unsupported_media_type"
         }
        }
       }
      }
     }
    }
   },
   "Unprocessable": {
    "description": "Unprocessable. Codes: IDEMPOTENCY_KEY_REUSED, SECRET_BLOCKED, URL_NOT_ALLOWED.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "idempotency_key_reused": {
        "summary": "IDEMPOTENCY_KEY_REUSED",
        "value": {
         "error": {
          "code": "IDEMPOTENCY_KEY_REUSED",
          "message": "This key was used with a different body. Use a new key.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#idempotency_key_reused"
         }
        }
       },
       "secret_blocked": {
        "summary": "SECRET_BLOCKED",
        "value": {
         "error": {
          "code": "SECRET_BLOCKED",
          "message": "The input contains one of our API keys; nothing was typed or run. Remove the key and resend.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#secret_blocked"
         }
        }
       },
       "url_not_allowed": {
        "summary": "URL_NOT_ALLOWED",
        "value": {
         "error": {
          "code": "URL_NOT_ALLOWED",
          "message": "The URL is not public HTTPS: it resolves to a private or reserved address, or redirects. Use a public HTTPS URL.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#url_not_allowed",
          "details": {
           "field": "url",
           "problem": "private_address"
          }
         }
        }
       }
      }
     }
    }
   },
   "TooManyRequests": {
    "description": "Rate limited. Codes: RATE_LIMITED.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     },
     "Retry-After": {
      "$ref": "#/components/headers/RetryAfter"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "rate_limited": {
        "summary": "RATE_LIMITED",
        "value": {
         "error": {
          "code": "RATE_LIMITED",
          "message": "Too many requests. Wait retry_after seconds.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": true,
          "retry_after": 2,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#rate_limited"
         }
        }
       }
      }
     }
    }
   },
   "InternalError": {
    "description": "Internal error. Codes: INTERNAL.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "internal": {
        "summary": "INTERNAL",
        "value": {
         "error": {
          "code": "INTERNAL",
          "message": "Our bug. Retry with the same Idempotency-Key.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": true,
          "retry_after": 1,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#internal"
         }
        }
       }
      }
     }
    }
   },
   "Unavailable": {
    "description": "Screen unavailable. Codes: SCREEN_UNAVAILABLE.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     },
     "Retry-After": {
      "$ref": "#/components/headers/RetryAfter"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "screen_unavailable": {
        "summary": "SCREEN_UNAVAILABLE",
        "value": {
         "error": {
          "code": "SCREEN_UNAVAILABLE",
          "message": "The screen's host or desktop is not answering. Retry after retry_after.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": true,
          "retry_after": 5,
          "outcome": "not_applied",
          "docs_url": "https://docs.trykubi.com/errors#screen_unavailable"
         }
        }
       }
      }
     }
    }
   },
   "OutcomeUnknown": {
    "description": "Outcome unknown. Codes: OUTCOME_UNKNOWN.",
    "headers": {
     "Request-Id": {
      "$ref": "#/components/headers/RequestId"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      },
      "examples": {
       "outcome_unknown": {
        "summary": "OUTCOME_UNKNOWN",
        "value": {
         "error": {
          "code": "OUTCOME_UNKNOWN",
          "message": "The work may or may not have happened. Look before acting; never resend blindly.",
          "request_id": "req_01jb7q0x9v2k8m4n6p3r",
          "retryable": false,
          "retry_after": null,
          "outcome": "unknown",
          "docs_url": "https://docs.trykubi.com/errors#outcome_unknown"
         }
        }
       }
      }
     }
    }
   }
  },
  "securitySchemes": {
   "bearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "API key",
    "description": "An API key from the dashboard (`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."
   }
  }
 }
}