openapi: 3.1.0 info: title: Wrenn API description: API for Wrenn — the runtime where AI coding agents live, work, and ship. version: 0.2.2 servers: - url: http://localhost:8080 description: Local development security: [] paths: /v1/capsules: post: summary: Create a capsule operationId: createCapsule tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateCapsuleRequest' responses: '202': description: Capsule creation initiated (status will be "starting") content: application/json: schema: $ref: '#/components/schemas/Capsule' '400': $ref: '#/components/responses/BadRequest' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/FailedPrecondition' '502': description: Host agent error content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' get: summary: List capsules for your team operationId: listCapsules tags: - capsules security: - apiKeyAuth: [] responses: '200': description: List of capsules content: application/json: schema: type: array items: $ref: '#/components/schemas/Capsule' /v1/capsules/stats: get: summary: Get capsule usage stats for your team operationId: getCapsuleStats tags: - capsules security: - apiKeyAuth: [] parameters: - name: range in: query required: false schema: type: string enum: - 5m - 1h - 6h - 24h - 30d default: 1h description: Time window for the time-series data. responses: '200': description: Capsule stats for the team content: application/json: schema: $ref: '#/components/schemas/CapsuleStats' '400': $ref: '#/components/responses/BadRequest' /v1/capsules/usage: get: summary: Get daily CPU and RAM usage for your team operationId: getCapsuleUsage tags: - capsules security: - apiKeyAuth: [] parameters: - name: from in: query required: false schema: type: string format: date description: Start date (YYYY-MM-DD). Defaults to 30 days ago. - name: to in: query required: false schema: type: string format: date description: End date (YYYY-MM-DD). Defaults to today. responses: '200': description: Daily usage data for the team content: application/json: schema: $ref: '#/components/schemas/UsageResponse' '400': $ref: '#/components/responses/BadRequest' /v1/capsules/{id}: parameters: - name: id in: path required: true schema: type: string get: summary: Get capsule details operationId: getCapsule tags: - capsules security: - apiKeyAuth: [] responses: '200': description: Capsule details content: application/json: schema: $ref: '#/components/schemas/Capsule' '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' delete: summary: Destroy a capsule operationId: destroyCapsule tags: - capsules security: - apiKeyAuth: [] responses: '202': description: Capsule destruction initiated '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/FailedPrecondition' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/exec: parameters: - name: id in: path required: true schema: type: string post: summary: Execute a command operationId: execCommand tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ExecRequest' responses: '200': description: Command output (foreground exec) content: application/json: schema: $ref: '#/components/schemas/ExecResponse' '202': description: Background process started content: application/json: schema: $ref: '#/components/schemas/BackgroundExecResponse' '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/processes: parameters: - name: id in: path required: true schema: type: string get: summary: List running processes operationId: listProcesses tags: - capsules security: - apiKeyAuth: [] description: 'Returns all running processes inside the capsule, including background processes and any processes started by templates or init scripts. ' responses: '200': description: Process list content: application/json: schema: $ref: '#/components/schemas/ProcessListResponse' '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/processes/{selector}: parameters: - name: id in: path required: true schema: type: string - name: selector in: path required: true description: Process PID (numeric) or tag (string) schema: type: string delete: summary: Kill a process operationId: killProcess tags: - capsules security: - apiKeyAuth: [] parameters: - name: signal in: query required: false description: Signal to send (SIGKILL or SIGTERM, default SIGKILL) schema: type: string enum: - SIGKILL - SIGTERM default: SIGKILL responses: '204': description: Process killed '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule or process not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/processes/{selector}/stream: parameters: - name: id in: path required: true schema: type: string - name: selector in: path required: true description: Process PID (numeric) or tag (string) schema: type: string get: summary: Stream process output via WebSocket operationId: connectProcess tags: - capsules security: - apiKeyAuth: [] description: 'Opens a WebSocket connection to stream stdout/stderr from a running background process. The selector can be a numeric PID or a string tag. Server sends JSON messages: - `{"type": "start", "pid": 42}` — connected to process - `{"type": "stdout", "data": "..."}` — stdout output - `{"type": "stderr", "data": "..."}` — stderr output - `{"type": "exit", "exit_code": 0}` — process exited - `{"type": "error", "data": "..."}` — error message ' responses: '101': description: WebSocket upgrade /v1/capsules/{id}/ping: parameters: - name: id in: path required: true schema: type: string post: summary: Reset capsule inactivity timer operationId: pingCapsule tags: - capsules security: - apiKeyAuth: [] description: 'Resets the last_active_at timestamp for a running capsule, preventing the auto-pause TTL from expiring. Use this as a keepalive for capsules that are idle but should remain running. ' responses: '204': description: Ping acknowledged, inactivity timer reset '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' /v1/capsules/{id}/metrics: parameters: - name: id in: path required: true schema: type: string get: summary: Get per-capsule resource metrics operationId: getCapsuleMetrics tags: - capsules security: - apiKeyAuth: [] description: 'Returns time-series CPU, memory, and disk metrics for a capsule. Three tiers are available with different granularity and retention: - `10m`: 500ms samples, last 10 minutes - `2h`: 30-second averages, last 2 hours - `24h`: 5-minute averages, last 24 hours For running capsules, data comes from the host agent''s in-memory ring buffer. For paused capsules, data is read from persisted snapshots in the database. Stopped/destroyed capsules return 404. ' parameters: - name: range in: query required: false schema: type: string enum: - 5m - 10m - 1h - 2h - 6h - 12h - 24h default: 10m description: Time range filter to query responses: '200': description: Metrics retrieved content: application/json: schema: $ref: '#/components/schemas/CapsuleMetrics' '400': description: Invalid range parameter content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Capsule not found or metrics not available content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/pause: parameters: - name: id in: path required: true schema: type: string post: summary: Pause a running capsule operationId: pauseCapsule tags: - capsules security: - apiKeyAuth: [] description: 'Takes a snapshot of the capsule (VM state + memory + rootfs), then destroys all running resources. The capsule exists only as files on disk and can be resumed later. ' responses: '202': description: Capsule pause initiated (status will be "pausing") content: application/json: schema: $ref: '#/components/schemas/Capsule' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/resume: parameters: - name: id in: path required: true schema: type: string post: summary: Resume a paused capsule operationId: resumeCapsule tags: - capsules security: - apiKeyAuth: [] description: 'Restores a paused capsule from its snapshot. Cloud Hypervisor is relaunched in --restore mode with memory_restore_mode=ondemand so guest pages fault in lazily via userfaultfd. The original network slot (and host-reachable IP) is preserved across pause/resume. ' responses: '202': description: Capsule resume initiated (status will be "resuming") content: application/json: schema: $ref: '#/components/schemas/Capsule' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '409': description: Capsule not paused content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/snapshots: post: summary: Create a snapshot template operationId: createSnapshot tags: - snapshots security: - apiKeyAuth: [] description: 'Snapshot a capsule, processed asynchronously. The call returns immediately with the capsule in the `snapshotting` state, then it returns to its original state on completion. The capsule must be `running` or `paused`. A `running` capsule is snapshotted live: it briefly pauses while its VM state + memory + flattened rootfs are written to a new template, then resumes to `running`. A `paused` capsule is snapshotted directly from its on-disk state without reviving the VM, and stays `paused`. Because it is async, the response does NOT contain the template. Watch for the `template.snapshot.create` SSE event (its `outcome` reports success or failure) or poll `GET /v1/snapshots` to observe completion. Snapshots are immutable: each call must use a fresh name. Re-using an existing name returns 409 Conflict. ' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSnapshotRequest' responses: '202': description: Snapshot accepted; capsule is now snapshotting content: application/json: schema: $ref: '#/components/schemas/Capsule' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '409': description: Name already exists, or capsule is not running or paused content: application/json: schema: $ref: '#/components/schemas/Error' get: summary: List templates for your team operationId: listSnapshots tags: - snapshots security: - apiKeyAuth: [] parameters: - name: type in: query required: false schema: type: string enum: - base - snapshot description: Filter by template type. responses: '200': description: List of templates content: application/json: schema: type: array items: $ref: '#/components/schemas/Template' /v1/snapshots/{name}: parameters: - name: name in: path required: true schema: type: string delete: summary: Delete a snapshot template operationId: deleteSnapshot tags: - snapshots security: - apiKeyAuth: [] description: Removes the snapshot files from disk and deletes the database record. responses: '204': description: Snapshot deleted '400': $ref: '#/components/responses/BadRequest' '403': description: Cannot delete a platform-owned / system base template content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Template not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Delete failed (e.g. owning host offline) content: application/json: schema: $ref: '#/components/schemas/Error' /v1/capsules/{id}/files/write: parameters: - name: id in: path required: true schema: type: string post: summary: Upload a file operationId: uploadFile tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: multipart/form-data: schema: type: object required: - path - file properties: path: type: string description: Absolute destination path inside the capsule file: type: string format: binary description: File content responses: '204': description: File uploaded '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '413': description: File too large content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/files/read: parameters: - name: id in: path required: true schema: type: string post: summary: Download a file operationId: downloadFile tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReadFileRequest' responses: '200': description: File content content: application/octet-stream: schema: type: string format: binary '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule or file not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/files/list: parameters: - name: id in: path required: true schema: type: string post: summary: List directory contents operationId: listDir tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ListDirRequest' responses: '200': description: Directory listing content: application/json: schema: $ref: '#/components/schemas/ListDirResponse' '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/files/mkdir: parameters: - name: id in: path required: true schema: type: string post: summary: Create a directory operationId: makeDir tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MakeDirRequest' responses: '200': description: Directory created content: application/json: schema: $ref: '#/components/schemas/MakeDirResponse' '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: 'Capsule not running, or a directory already exists at the target path (error code `already_exists`). ' content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/files/remove: parameters: - name: id in: path required: true schema: type: string post: summary: Remove a file or directory operationId: removePath tags: - capsules security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RemoveRequest' responses: '204': description: File or directory removed '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/exec/stream: parameters: - name: id in: path required: true schema: type: string get: summary: Stream command execution via WebSocket operationId: execStream tags: - capsules security: - apiKeyAuth: [] description: 'Opens a WebSocket connection for streaming command execution. **Client sends** (first message to start the process): ```json {"type": "start", "cmd": "tail", "args": ["-f", "/var/log/syslog"]} ``` **Client sends** (to stop the process): ```json {"type": "stop"} ``` **Server sends** (process events as they arrive): ```json {"type": "start", "pid": 1234} {"type": "stdout", "data": "line of output\n"} {"type": "stderr", "data": "warning message\n"} {"type": "exit", "exit_code": 0} {"type": "error", "data": "description of error"} ``` The connection closes automatically after the process exits. Note: capsule-not-found and not-running conditions encountered after the WebSocket upgrade are delivered as in-band `{"type": "error"}` frames rather than HTTP status codes. ' responses: '101': description: WebSocket upgrade '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' /v1/capsules/{id}/pty: parameters: - name: id in: path required: true schema: type: string get: summary: Interactive PTY session via WebSocket operationId: ptySession tags: - capsules security: - apiKeyAuth: [] description: "Opens a WebSocket connection for an interactive PTY (terminal) session.\nSupports creating new sessions,\ \ sending input, resizing, killing, and\nreconnecting to existing sessions.\n\n**Client sends** (first message — start\ \ a new PTY):\n```json\n{\n \"type\": \"start\",\n \"cmd\": \"/bin/bash\",\n \"args\": [],\n \"cols\": 80,\n \ \ \"rows\": 24,\n \"envs\": {\"TERM\": \"xterm-256color\"},\n \"cwd\": \"/home/user\",\n \"user\": \"user\"\n}\n\ ```\nAll fields except `type` are optional. Defaults: cmd=\"/bin/bash\", cols=80, rows=24.\n\n**Client sends** (first\ \ message — reconnect to existing PTY):\n```json\n{\"type\": \"connect\", \"tag\": \"pty-abc123de\"}\n```\n\n**Client\ \ sends** (after session is established):\n```json\n{\"type\": \"input\", \"data\": \"\"}\n\ {\"type\": \"resize\", \"cols\": 120, \"rows\": 40}\n{\"type\": \"kill\"}\n```\n\n**Server sends**:\n```json\n{\"\ type\": \"started\", \"tag\": \"pty-abc123de\", \"pid\": 42}\n{\"type\": \"output\", \"data\": \"\"}\n{\"type\": \"exit\", \"exit_code\": 0}\n{\"type\": \"error\", \"data\": \"description\", \"fatal\": true}\n\ {\"type\": \"ping\"}\n```\n\nPTY data (input and output) is base64-encoded because it contains raw\nterminal bytes\ \ (escape sequences, control codes) that are not valid UTF-8.\n\nSessions persist across WebSocket disconnections\ \ — the process keeps\nrunning in the capsule. Use the `tag` from the \"started\" response to\nreconnect later.\n" responses: '101': description: WebSocket upgrade '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' /v1/capsules/{id}/files/stream/write: parameters: - name: id in: path required: true schema: type: string post: summary: Upload a file (streaming) operationId: streamUploadFile tags: - capsules security: - apiKeyAuth: [] description: 'Streams file content to the capsule without buffering in memory. Suitable for large files. Uses the same multipart/form-data format as the non-streaming upload endpoint. ' requestBody: required: true content: multipart/form-data: schema: type: object required: - path - file properties: path: type: string description: Absolute destination path inside the capsule file: type: string format: binary description: File content responses: '204': description: File uploaded '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '502': description: Host agent error while streaming the upload content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/capsules/{id}/files/stream/read: parameters: - name: id in: path required: true schema: type: string post: summary: Download a file (streaming) operationId: streamDownloadFile tags: - capsules security: - apiKeyAuth: [] description: 'Streams file content from the capsule without buffering in memory. Suitable for large files. Returns raw bytes with chunked transfer encoding. ' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReadFileRequest' responses: '200': description: File content streamed in chunks content: application/octet-stream: schema: type: string format: binary '400': $ref: '#/components/responses/BadRequest' '404': description: Capsule or file not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Capsule not running content: application/json: schema: $ref: '#/components/schemas/Error' '503': $ref: '#/components/responses/ServiceUnavailable' /v1/events/stream: get: summary: Real-time lifecycle event stream operationId: streamEvents tags: - events description: 'Server-Sent Events stream of capsule, template, and host lifecycle events scoped to the caller''s active team. Browsers send the wrenn_sid cookie automatically on EventSource connections; SDKs authenticate via X-API-Key. Frame format follows the standard SSE protocol: ``` event: capsule.create data: {"event":"capsule.create","outcome":"success","resource":{"id":"sb-..."},"sandbox":{...},"timestamp":"2026-05-19T02:00:00Z"} : keepalive ``` A `: keepalive` comment is emitted every 30s. ' security: - apiKeyAuth: [] responses: '200': description: SSE stream opened content: text/event-stream: schema: $ref: '#/components/schemas/SSEEvent' '401': description: Missing or invalid auth content: application/json: schema: $ref: '#/components/schemas/Error' components: responses: BadRequest: description: Invalid request parameters content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Authenticated but not permitted (e.g. non-admin on /v1/admin/*) content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' FailedPrecondition: description: Resource state does not allow this operation (e.g. exec on a paused capsule) content: application/json: schema: $ref: '#/components/schemas/Error' ServiceUnavailable: description: The host serving this capsule is unreachable (host_unavailable) content: application/json: schema: $ref: '#/components/schemas/Error' securitySchemes: apiKeyAuth: type: apiKey in: header name: X-API-Key description: API key for capsule lifecycle operations. Create via POST /v1/api-keys. schemas: CreateCapsuleRequest: type: object properties: template: type: string default: minimal-ubuntu vcpus: type: integer default: 1 memory_mb: type: integer default: 512 timeout_sec: type: integer minimum: 0 default: 0 description: 'Auto-pause TTL in seconds. The capsule is automatically paused after this duration of inactivity (no exec or ping). 0 means no auto-pause. Positive values below 60 are silently clamped to 60 (the agent''s startup envelope). ' metadata: type: object additionalProperties: type: string nullable: true description: 'Optional user-supplied key/value labels attached to the capsule. Max 20 keys; keys match [a-zA-Z0-9][a-zA-Z0-9._-]{0,63}; values are at most 64 characters. Reserved system keys (kernel_version, vmm_version, agent_version, envd_version) are rejected. Metadata is set at create-time only. ' UsageResponse: type: object properties: from: type: string format: date to: type: string format: date points: type: array items: type: object properties: date: type: string format: date cpu_minutes: type: number ram_mb_minutes: type: number CapsuleStats: type: object properties: range: type: string enum: - 5m - 1h - 6h - 24h - 30d current: type: object properties: running_count: type: integer vcpus_reserved: type: integer memory_mb_reserved: type: integer peaks: type: object description: Maximum values over the last 30 days. properties: running_count: type: integer vcpus: type: integer memory_mb: type: integer series: type: object description: Parallel arrays for chart rendering. properties: labels: type: array items: type: string format: date-time running: type: array items: type: integer vcpus: type: array items: type: integer memory_mb: type: array items: type: integer Capsule: type: object properties: id: type: string status: type: string enum: - pending - starting - running - pausing - paused - snapshotting - resuming - stopping - hibernated - stopped - missing - error template: type: string vcpus: type: integer memory_mb: type: integer timeout_sec: type: integer created_at: type: string format: date-time started_at: type: string format: date-time nullable: true last_active_at: type: string format: date-time nullable: true last_updated: type: string format: date-time metadata: type: object additionalProperties: type: string nullable: true description: 'User-supplied key/value labels attached at create-time. Once the capsule is running this also carries agent-side system version info (kernel_version, vmm_version, agent_version, envd_version); those reserved keys are owned by the system and cannot be set by users. ' disk_size_mb: type: integer description: Maximum disk capacity in MiB. disk_used_mb: type: integer format: int64 description: Current disk usage in MiB. Only populated on individual capsule GET; omitted in list responses. CreateSnapshotRequest: type: object required: - sandbox_id properties: sandbox_id: type: string description: ID of the running capsule to snapshot. name: type: string description: Name for the snapshot template. Auto-generated if omitted. Template: type: object properties: name: type: string type: type: string enum: - base - snapshot vcpus: type: integer nullable: true memory_mb: type: integer nullable: true size_bytes: type: integer format: int64 created_at: type: string format: date-time platform: type: boolean description: 'True when the template is platform-managed (visible to all teams, e.g. the built-in `minimal-ubuntu` rootfs). False for team-owned snapshot templates. ' protected: type: boolean description: 'True for built-in system base templates (minimal-ubuntu, minimal-alpine, minimal-arch, minimal-fedora). Protected templates cannot be deleted. ' metadata: type: object additionalProperties: type: string nullable: true ExecRequest: type: object required: - cmd properties: cmd: type: string args: type: array items: type: string timeout_sec: type: integer default: 30 description: Timeout in seconds (foreground exec only, default 30) background: type: boolean default: false description: If true, starts the process in the background and returns immediately with a PID and tag (HTTP 202) tag: type: string description: Optional user-chosen tag for the background process. Auto-generated if omitted. Only used when background is true. envs: type: object additionalProperties: type: string description: Environment variables for the process (applies to both foreground and background exec) cwd: type: string description: Working directory for the process (applies to both foreground and background exec) BackgroundExecResponse: type: object properties: sandbox_id: type: string cmd: type: string pid: type: integer tag: type: string ProcessEntry: type: object properties: pid: type: integer tag: type: string cmd: type: string args: type: array items: type: string ProcessListResponse: type: object properties: processes: type: array items: $ref: '#/components/schemas/ProcessEntry' ExecResponse: type: object properties: sandbox_id: type: string cmd: type: string stdout: type: string stderr: type: string exit_code: type: integer duration_ms: type: integer encoding: type: string enum: - utf-8 - base64 description: Output encoding. "base64" when stdout/stderr contain binary data. ReadFileRequest: type: object required: - path properties: path: type: string description: Absolute file path inside the capsule ListDirRequest: type: object required: - path properties: path: type: string description: Directory path inside the capsule depth: type: integer default: 1 description: Recursion depth (0 = non-recursive, 1 = immediate children) ListDirResponse: type: object properties: entries: type: array items: $ref: '#/components/schemas/FileEntry' FileEntry: type: object properties: name: type: string path: type: string type: type: string enum: - file - directory - symlink size: type: integer format: int64 mode: type: integer permissions: type: string description: Human-readable permissions (e.g. "-rwxr-xr-x") owner: type: string group: type: string modified_at: type: integer format: int64 description: Unix timestamp (seconds) symlink_target: type: string nullable: true MakeDirRequest: type: object required: - path properties: path: type: string description: Directory path to create inside the capsule MakeDirResponse: type: object properties: entry: $ref: '#/components/schemas/FileEntry' RemoveRequest: type: object required: - path properties: path: type: string description: Path to remove inside the capsule CapsuleMetrics: type: object properties: sandbox_id: type: string range: type: string enum: - 5m - 10m - 1h - 2h - 6h - 12h - 24h points: type: array items: $ref: '#/components/schemas/MetricPoint' MetricPoint: type: object properties: timestamp_unix: type: integer format: int64 cpu_pct: type: number format: double description: CPU utilization percentage (0-100), normalized to vCPU count mem_bytes: type: integer format: int64 description: Resident memory in bytes (VmRSS of Cloud Hypervisor process) disk_bytes: type: integer format: int64 description: Allocated disk bytes for the CoW sparse file Error: type: object properties: error: type: object properties: code: type: string message: type: string SSEEvent: type: object description: 'Wire format of one SSE message body. The event name (`event:` line) is the `kind` and the JSON below is the `data:` line. ' properties: event: type: string enum: - connected - capsule.create - capsule.pause - capsule.resume - capsule.destroy - capsule.state.changed - template.snapshot.create - template.snapshot.delete - host.up - host.down outcome: type: string enum: - success - error description: 'Present for action events (capsule.* except state.changed, template.snapshot.*). Absent for host.up/down, capsule.state.changed, and the connected sentinel. ' resource: type: object properties: id: type: string type: type: string actor: type: object properties: type: type: string enum: - user - api_key - system id: type: string name: type: string metadata: type: object additionalProperties: type: string description: 'Event-specific context. Examples: `reason` (ttl_expired, host_failure, cleanup_after_create_error, orphaned), `host_ip`, `from`/`to` (for capsule.state.changed). ' error: type: string description: Failure reason; only set when outcome=error. sandbox: allOf: - $ref: '#/components/schemas/Capsule' nullable: true description: Populated for capsule.* events; null if DB lookup failed. timestamp: type: string format: date-time