Types

The shapes the responses are made of. A field typed Vnc or Schedule in the reference is one of these.

Activity

Fields

activity_idalwaysstring

Immutable activity ID; a request identity, never an execution idempotency key.

account_idalwaysstring
computer_idalwaysstring
workspace_idalwaysstring or null

The actual workspace when the request was admitted.

channelalways"api" | "session-api" | "managed-agent"

Who sent the request. api: an API key, or a connected app acting through one. session-api: the dashboard, signed in. managed-agent: a hosted agent run — its summary row (action agent), and a row for each input or bash command it sent to the desktop.

routealways"input" | "exec" | "window" | "clipboard" | "agent"
actionalways"agent" | "input" | "move" | "click" | "button-down" | "button-up" | "drag" | "scroll" | "type" | "paste" | "key" | "hold-key" | "exec" | "background-exec" | "window" | "focus" | "raise" | "minimize" | "maximize" | "unmaximize" | "close" | "window-move" | "resize" | "clipboard"
statealways"pending" | "acknowledged" | "exited" | "accepted" | "refused" | "unknown"
received_atalwaysstring

Control-plane receipt time, UTC.

observed_atalwaysstring

Control-plane observation time, UTC.

revisioninteger

Monotonic row revision, including late result links. Does not change action observation time.

has_resultsboolean

Small link hint; expand the passive results detail for current availability.

fundedboolean

Present on managed-agent run summaries: whether the run used account AI credits.

cost_usdnumber

Total funded AI charge on a managed-agent run summary. Zero for BYOK, whose model bill is external.

dispatched_atstring

First physical eligible transport attempt, when observed. Does not prove guest execution.

elapsed_msinteger

Monotonic request elapsed time, including network and admission; not guest execution duration.

reason"body-too-large" | "invalid-body" | "local-refusal" | "transport" | "unclassified" | "invalid-result" | "result-too-large" | "wait-ended" | "pending-expired"
http_statusinteger

Known response status. A dispatched error does not prove the action had no effect.

exit_codeinteger

Present only for a known synchronous command exit, including signed failures. Timeout sentinel values are omitted.

execution_idstring

Optional immutable execution ID returned by the original request; never inferred from PID. No output is fetched to populate history.

ActivityHealth

Fields

recording_started_atalwaysstring

When this store enabled recording; earlier requests are not reconstructed.

earliest_retained_atalwaysstring or null

Earliest retained row in the current read scope.

count_truncatedalwaysboolean

Account history has crossed a row-count retention boundary.

age_truncatedalwaysboolean

Account history has crossed an age retention boundary.

capturealways"available" | "degraded"
completenessalways"best-effort"

Operational history, without an audit-grade completeness guarantee, including across process loss.

gap_atalwaysstring or null

Most recent account-level capture gap within retention. It identifies no unverified computer.

recovered_atalwaysstring or null

Last observed recovery from a capture gap.

AgentResult

Fields

cost_usdnumber

Cumulative AI credit charge in USD, present on funded runs only.

stepsalwaysinteger

How many actions the loop took — a count; the actions themselves are steps_taken.

stopalwaysstring

Why the loop ended: end_turn (the model finished), max_steps (it reached max_steps), refusal (the model declined), pause_turn (the model kept pausing its turn), rate_limited (your rate budget ran out mid-run), credits_exhausted (funded credits unavailable), max_cost (the next reservation would exceed max_cost_usd), or another stop reason the model itself gave, such as max_tokens. OpenAI also reports incomplete for an unfinished response and safety_check when action approval is needed. Only end_turn means the task was finished; the others are a run that stopped, and the work it did to the desktop stands.

textalwaysstring

The model’s closing text — its answer, or why it could not get there.

usagealwaysAgentUsage
thread_idstring

The thread this exchange was kept in; send it as thread_id to continue. Absent with store: false.

steps_takenAgentStep[]

Every action, in order. On the stream: false body only; the stream’s done event carries the other fields, and each action arrived before it as a step event.

AgentRun

Fields

idalwaysstring

run_ and twenty-four hex characters.

computer_idalwaysstring
thread_idalwaysstring or null

The thread the run is kept in, once it is: null until a new thread has something in it (a run that has not acted yet, or failed before it did), and when the thread was deleted. Continue it with thread_id on the next run.

statusalways"running" | "completed" | "failed" | "cancelled" | "interrupted"

completed: the loop ended on its own, and result.stop says how — only end_turn means the task was finished. failed: see error. cancelled: stopped by a cancel. interrupted: the control plane restarted while it was going (error.status 503), or the run stopped without its ending being recorded (error.status 500); it was not resumed.

promptalwaysstring

The prompt, at most 32,000 characters of it.

modelalwaysstring

The model the run was started with.

max_stepsalwaysinteger
stepsalwaysinteger

How many actions it has taken so far.

cost_usdnumber

Funded runs: the charge so far, in USD.

resultAgentResult
errorobject
steps_takenalwaysAgentStep[]

Every action so far, in order. Empty in a list.

created_atalwaysstring
updated_atalwaysstring

When it last took a step or changed status.

ended_atalwaysstring or null

When it ended; null while running.

AgentStep

Fields

cost_usdnumber

Cumulative funded run charge in USD. Multiple tools in one reply repeat the same total; do not sum these fields.

credits_remaining_usdnumber

Available account AI credits after this model call; may be negative after an overrun. Omitted if the post-debit balance lookup is unavailable.

nalwaysinteger

Its position in the run, from 1.

toolalwaysstring

The tool the model reached for, e.g. computer or bash.

actionstring

The provider-native action within it, e.g. left_click, zoom or OpenAI click. Absent for bash.

detailstring

What the platform did with it, in one line.

errorstring

Present when an action failed or was refused. On an Anthropic run the model is told and the loop goes on. On an OpenAI run a failed batch or bash command ends the run as a failure, after the actions the desktop acknowledged; input is not replayed.

AgentThreadSummary

Fields

idalwaysstring

thr_ and twenty-four hex characters.

computer_idalwaysstring

The computer its runs drove. A thread is continued only there.

titlealwaysstring

The first line of the prompt of the first run kept in it (not of any messages it was started with), at most 80 characters.

turnsalwaysinteger

How many turns it holds: a prompt and an answer per run, plus any history it was started with.

created_atalwaysstring
updated_atalwaysstring

When a run last used it: kept an exchange in it, or began continuing it.

AgentThreadTurn

Fields

rolealways"user" | "assistant"
textalwaysstring

The prompt, or the run’s closing text. At most 32,000 characters are kept.

stopstring

Assistant turns: how the run ended, as AgentResult.stop, or error or aborted; running while its run is going, and interrupted when the control plane restarted before it finished. running, aborted and interrupted are runs that may have done more than they reported.

errorstring

Assistant turns of a failed run: why it failed.

stepsinteger

Assistant turns: how many actions the run took.

cost_usdnumber

Assistant turns of a funded run: what it cost.

created_atalwaysstring

AgentUsage

Fields

input_tokensalwaysinteger

Input tokens, the cache reads and writes below included.

output_tokensalwaysinteger
cache_read_tokensalwaysinteger
cache_write_tokensalwaysinteger

Provider-reported cache writes; zero only when absent. OpenAI reads input_tokens_details.cache_write_tokens.

ApiKey

Fields

idalwaysstring

key- and twelve hex characters. What an audit entry names as apikey:<id>.

namealwaysstring or null

Its label, up to 60 characters, or null when it was minted without one. For a hosted-MCP (OAuth) credential, which only GET /whoami answers, Connected app: followed by the connected app’s name, up to 100 characters in all.

prefixalwaysstring

The first twelve characters of the key and an ellipsis, for recognising it. Not a secret. oauth for a hosted-MCP (OAuth) credential, as GET /whoami answers it.

created_atalwaysstring

RFC 3339 timestamp.

last_used_atalwaysstring or null

RFC 3339 timestamp of its last authenticated request, or null until it has one.

workspace_idalwaysstring or null

The workspace it is confined to, or null for the whole account.

workspace_namealwaysstring or null

That workspace’s name, or null for an account-wide key.

manage_keysalwaysboolean

Whether this key may use /api-keys. With it a key lists its holder’s keys; creating and revoking also need the holder to be a member or owner, so a viewer’s key can only list. Set only from the dashboard; no key can set it.

minted_by_key_idalwaysstring or null

The id of the API key that minted this one over the API, or null for a key minted from the dashboard. Kept after that key is revoked.

BrowserProxy

Fields

serveralwaysstring

The proxy, as http://host:port, https://host:port or socks5://host:port, lower-cased as stored. The host is a name or an IPv4 address, the port is explicit, and there is nothing else in it: no path, and never a username or password — a URL carrying one is refused, because this setting is returned by every read of the computer; give an http:// proxy its password with credentials_secret_id instead. https:// is TLS to the proxy itself, for a proxy that listens for it. HTTPS sites are tunnelled end to end through an http:// proxy too; https:// also encrypts, between the browser and the proxy, which sites are visited and any plain-HTTP page. With https://, Chromium is given the proxy as it is, and Firefox — whose settings cannot name a TLS proxy — a proxy auto-config (PAC) script at /etc/mandala/browser-proxy.pac that its locked settings point at, answering HTTPS host:port and applying bypass exactly as Chromium does (with no DNS lookup: an address range matches only a URL whose host is an address), and it does not fall back to connecting directly when the proxy is down. Private, link-local (including the cloud metadata address), CGNAT, multicast and unspecified addresses are refused, since a computer cannot reach them; 127.0.0.1 and localhost are allowed and mean the computer itself, so a forwarder of your own running on it can be the proxy.

credentials_secret_idstring

For an upstream that asks for a username and password: the id of a secret in your store whose value is user:password (a trailing newline is ignored). Only with an http:// proxy for now, and not one on the computer’s own loopback. The computer holds the id, never the value, and the value reaches it one way only — the secret must be bound to the computer as a FILE, {"secret_id": …, "file": "<name>"} in its secrets (at create, or with PUT /computers/{id}/secrets), or the setting is refused with 400. So everything a bound secret does, this does: it is delivered as the computer starts, a computer’s first secrets can be bound only while it is stopped, a binding added to a running computer arrives at its next restart, and replacing the secret’s value reaches a running computer within seconds where its image takes live file updates (otherwise at its next start). While the setting names it, a change to the computer’s secrets that drops that file binding is refused with 409. The browsers are pointed at a forwarder on the computer’s loopback, 127.0.0.1:29380, which reads /run/mandala-secrets/user/files/<name> on every connection and sends the upstream Proxy-Authorization: Basic; bypass applies as without credentials.

bypassstring[]

Hosts the browser reaches directly, at most 64: example.com (that host), *.example.com or .example.com (its subdomains), an IPv4 address, a range such as 192.0.2.0/24 or fd00::/8, or <local> (names with no dot). Stored in a canonical spelling, which a read returns: trimmed and lower-cased, .example.com as *.example.com, a range masked to its network (192.0.2.7/24 is 192.0.2.0/24), and a bare IPv6 address as its /128; entries that are then the same are kept once. A range matches a URL whose host is an address in it, never a name that resolves into it. localhost and loopback addresses always bypass the proxy. So do link-local addresses (169.254.0.0/16, fe80::/10) in Chromium, and in Firefox with an https:// proxy.

Build

Fields

startedstring

RFC 3339 timestamp.

sourcestring

What the disk is being copied from.

failedstring

Why the build failed, when it did.

BuildStep

Fields

ninteger

Its position, 1-based.

kindstring

apt, run, file, mkdir, env for the environment block, or finish for the cleanup every build ends with.

labelstring

What the step does, from your own document — the packages, the path, or the first real line of the script.

statusstring

pending, running, done, failed, or skipped for one an earlier failure meant we never reached.

unknown is the rare one: this build’s step record was lost mid-build — a host that lost power between the write and the flush — and rebuilt from your document, so these steps ran and what became of them is not recoverable. It is not reported as done, which would claim they succeeded, or as pending, which would claim they never ran.

started_atstring

RFC 3339. Absent until it starts.

finished_atstring

RFC 3339. Absent until it ends.

ChatCompletionChoice

Fields

indexalwaysinteger

Always 0.

messagealwaysobject
finish_reasonalways"stop" | "length" | "content_filter"

Our agent.stop in OpenAI’s vocabulary: max_steps, max_tokens, model_context_window_exceeded, pause_turn and incomplete are length, refusal and safety_check are content_filter, and everything else is stop — rate_limited included, so a run that ran out of rate budget reports stop like a finished one. Read agent.stop to tell them apart.

Computer

Fields

idstring

Stable identifier, e.g. vm-a1b2c3d4e5f6.

namestring

The name you gave it.

statusstring

running, stopped, suspended, building, build-failed or half-removed. The last is a computer whose deletion stopped partway and took its disk with it: every call that needs a disk is refused on it, it will never start again, and deleting it again is what clears it. A client that treats an unknown status as stopped will offer a start this API always refuses, so read these as a closed set and everything outside it as not startable. A running computer can be paused by its host without its status changing. When the host is low on disk space, the guests writing fastest are paused, and when it is critically low every running computer on it is paused, including ones that wrote nothing. Screenshots, input and commands stall or fail meanwhile, and the computer resumes by itself once space recovers. A suspend or memory snapshot that fails can also leave it paused: always when the failure says “this computer remains paused because the host is low on disk space”, and other failures can, at any free space. While the platform holds it for disk-space recovery, start, restart, suspend and snapshot answer 503 (“waiting for disk-space recovery”) until it resumes by itself once that recovery can finish, and a stop discards the paused session. Nor does any computer start or resume on a host that is low on disk space: a start, a restart of a stopped computer and the resume that using a suspended computer sets off answer 503 (“this host is low on disk space…”), and a restore answers 503 too, until space on that host has been freed.

osstring

linux or windows.

desktopstring

The display protocol this computer’s desktop speaks: wayland, x11, or absent (X11, or a host too old to say); branch on === "wayland". Screenshots, input and exec behave identically either way. What differs on a Wayland computer is that a window id is the compositor’s address rather than an X window id — opaque and handed back the same way, but not comparable across the two — and that moving or resizing a window the compositor is TILING is refused rather than silently ignored, because a tiled window’s geometry belongs to the layout. Read it here rather than from the template: a computer keeps the image it was built from, and a template’s version can advance underneath it.

templatestring

The template this computer was built from.

cpuinteger

Cores.

ram_mbinteger
disk_gbinteger
running_ram_mbinteger

The guest memory this computer is holding against your plan’s running pool: ram_mb while a process is live, or while a start or resume has been admitted and has not launched yet, and 0 otherwise. Non-zero BEFORE status says running, which is the point of publishing it — admission reserves the memory, and status is read from the guest process, which does not exist yet. So stopped with a non-zero value here is a cold boot on its way up, and suspended with one is a resume on its way up; 0 beside either means nothing has been admitted. Not proof that nothing will be: a start still waiting on this computer’s lifecycle lock has reserved nothing yet. ABSENT rather than 0 wherever this API cannot say — a host that could not be reached, a response written before the computer was read back (a create that answers with a start error, PATCH, either clone), or a host too old to report it. Absence means unknown, never zero.

resolutionstring

The screen this guest renders at, as WIDTHxHEIGHTxDEPTH. This is the coordinate space every click and every screenshot is in — size your model prompts from it rather than by decoding a PNG. Chosen at create and fixed for the life of the computer.

workspace_idstring

The workspace this computer is in. Absent when it is in none. It can change: a stopped computer can be moved to another workspace, or to none, with PUT /computers/{id}/workspace or from the dashboard.

created_atstring

RFC 3339 timestamp.

buildBuild

Present only while status is building or build-failed.

suspendedSuspended

Present only while status is suspended.

idle_suspend_mininteger

How long this computer may go untouched before its host suspends it. Absent on a computer with no override, which follows whatever its host is sweeping at. The host default is deliberately not reported in its place — it is a property of the host and it changes when an operator changes it.

snapshot_scheduleSchedule
browser_proxyBrowserProxy

The proxy this computer’s browsers are sent through — Chromium, Chrome where it is installed, and Firefox — as a managed policy each reads when it starts, locked so the browser’s own settings cannot override it. Only the browsers: exec, a terminal and every other program reach the network directly. Absent when none is set. Set at create or with PATCH /computers/{id}; a clone does not inherit it.

egress_proxyEgressProxy

The proxy ALL of this computer’s outbound TCP is sent through, taken on its host at the computer’s network port, so nothing inside the computer is configured and nothing there can opt out: exec, terminals, package managers and browsers alike. Each connection is tunnelled to the host and port it was going to, by name where the connection says one (the TLS server name, or an HTTP Host) so the proxy resolves it, and otherwise by address. It FAILS CLOSED: when the proxy is down or refuses, the connection fails and nothing is sent directly. UDP to the internet is dropped (so QUIC falls back to TCP, and NTP and other UDP do not work), as is ICMP. DNS lookups still go to the platform’s resolver and are not proxied. Connections through the proxy are closed when the setting changes, and when the computer stops. One owner’s computers on a host hold at most 512 proxied connections at once together (256 through an https:// proxy), and fewer when the host is busy with other owners’; past that a new connection is closed at once. A connection whose server speaks first (SMTP, MySQL, an FTP control channel) waits about 250 ms for the computer to send first, then is tunnelled by address. A connection one side has finished is closed after 60 seconds of silence from the other side. Absent when none is set. Set at create or with PATCH /computers/{id}; a clone does not inherit it.

egress_proxy_pendingboolean

Present and true while a running computer’s egress_proxy names credentials_secret_id and its host does not hold that secret’s value yet — just after a create or a change, after the host restarts, or once the secret is deleted. Every connection the computer opens meanwhile is closed, never sent directly; it usually clears within seconds. It does NOT clear by itself once the computer’s workspace is deleted: its egress credentials then resolve account-wide only, so a proxy naming one of that workspace’s own secrets is held closed until it is saved again naming an account-wide secret. Never present on a computer that is not running.

browser_proxy_pendingboolean

Present and true while a running computer’s guest does not have its browser_proxy yet — just after a create, a start or a change — or still has the files of one just removed. Wait for it to go away before starting a browser that must use the proxy. A browser already running when the setting changes applies it at its next start (Chromium usually within seconds). Never present on a computer that is not running: the setting is applied as it starts.

statestring

Whether this computer exists, as the platform’s own record has it — a different question from status, which is what its host says it is doing. live: its host lists it. unreachable: its host has not answered — on a listing row, for this request; the computer is most likely fine and nothing has been done to it. deleting: a delete was sent and not yet answered. deleted and lost are terminal, and are only ever shown when asked for with state=: deleted is a delete that was answered, lost is a computer whose host was written off by an operator while it was unreachable. Present on listing rows; absent on a single computer, which is served by its host and is therefore live.

deleted_atstring

RFC 3339 timestamp of the answered delete. Present once state has been deleted.

lost_atstring

RFC 3339 timestamp of the write-off. Present once state has been lost.

unreachableboolean

Present and true only on a row the platform served from its own record because the host holding this computer could not be reached. Such a row carries the computer’s identity — name, os, template, size, workspace_id, created_at, state — and nothing only its host knows: no status, no resolution. Its state is unreachable, or deleting when a delete is in flight for it. A listing with any such row carries X-GC-Incomplete, so you only ever see this after asking for a partial answer with allow_partial=1.

secretsobject[]

The secrets bound to this computer — at create, or since with PUT /computers/{id}/secrets — as references: which secret, which revision of it, and the environment variable or file each lands under. Never a value. Present only on a computer that holds secrets; every field in this group is absent on one that does not, except secrets_pending while it is true. On any host that seals saved sessions a computer that holds secrets restarts — by this API or from inside it — and gets its secrets again as it comes back up; a restart of a stopped one is refused (409, reason: "unavailable"): start it instead, which delivers its secrets. It suspends — by hand or when idle — and can be memory-snapshotted. A disk snapshot restored onto it starts it with its secrets delivered afresh; a memory snapshot restored onto it resumes holding what it held when the snapshot was taken, and is refused if it was taken without sealing or the platform will no longer open it (see POST /snapshots/{id}/restore). Taking disk snapshots, cloning, moving between hosts and deletion work as usual, and a clone starts with no secrets bound, unless it is a fork of one of its memory snapshots made with inherit_secrets: true.

secrets_generationinteger

How many delivering starts this computer has had. Each cold start of a computer that holds secrets is one generation; the receipt below names the generation it is for, so a receipt whose generation is behind this one is from an earlier start.

secrets_appliedobject

The receipt that the bound values reached this computer’s desktop session: which start, when, and which revision of each secret. This is the confirmation — a start that was taken but whose delivery did not land has no receipt for its generation and secrets_error says why. Absent before the first delivery.

secrets_errorstring

Why the last delivering start was stopped, in one fixed sentence naming the class of failure and never a value. A start whose delivery fails is stopped by the host and stays stopped until you start it again; this is the reason. Cleared when the next delivering start begins. Absent when the last delivery landed or none has been tried.

secrets_pendingboolean or null

Whether this computer’s desktop is missing a change to its secrets that a restart would deliver: a binding, an env name or file, or a newer value it does not hold — or, once every secret has been removed, whether its running or saved session still holds the values it had. Compared against the receipt, so it clears once any delivery lands — a start, a restart from anywhere, a reboot inside it, or a secret file replaced while it runs — and stays after one that failed. A value replaced for a secret bound as a file is replaced live on a running computer, and is pending only if that did not land. One bound as an environment variable is the same on an image that hands new shells the current values: once they see it, it is not pending, though programs already running — the desktop session among them — keep the value they started with until a restart; on any other image it waits for a restart. A change that removed a secret, or moved one to another revision, stays pending until a restart either way, since the running session may still hold the old value. Only ever true of a running or suspended computer: a suspended one resumes with the values it had, and a stopped one gets everything current when it starts, so it is never pending. null when it could not be checked — the current values could not be read, or the computer’s last delivery predates the record of the names it was made under and it has been rebound since; a restart makes it known. A delivery still on its way is not pending — wait on secrets_delivering for that. Present whenever secrets is, and on a computer with no secrets bound only while true.

secrets_deliveringboolean

Whether values are on their way into this computer’s desktop right now. A computer comes back running a few seconds before its secrets land, and a command run in between sees them unset; after a create, a start or a restart, wait for this to be false before relying on them. Also true from the moment a restart, or a reboot inside the computer, clears them until they are delivered again, and while a replaced value is written to a file. Reported by the computer’s host, which is doing the delivering. False on a computer that is not running and after a delivery that failed (secrets_error says why, and the computer is stopped). After a restart or a reboot it can also go back to false with nothing delivered and no secrets_error: that is a computer that has not come back up three minutes after it, and its secrets are not in it — restart it, or stop and start it. While it reads true only because a restarted computer has not come back up yet, a further restart is accepted, since that is the way out of one that does not; once the delivery itself has begun, a restart answers 409 until it lands. Present whenever secrets is.

ComputerConnect

A computer, plus what is needed to open its desktop. Carried by every response that is ONE computer — create, clone, single GET, PATCH — so connecting never costs a second call. Deliberately absent from the list response: a credential in every list is a credential in every log line that ever captured one.

Fields

idstring

Stable identifier, e.g. vm-a1b2c3d4e5f6.

namestring

The name you gave it.

statusstring

running, stopped, suspended, building, build-failed or half-removed. The last is a computer whose deletion stopped partway and took its disk with it: every call that needs a disk is refused on it, it will never start again, and deleting it again is what clears it. A client that treats an unknown status as stopped will offer a start this API always refuses, so read these as a closed set and everything outside it as not startable. A running computer can be paused by its host without its status changing. When the host is low on disk space, the guests writing fastest are paused, and when it is critically low every running computer on it is paused, including ones that wrote nothing. Screenshots, input and commands stall or fail meanwhile, and the computer resumes by itself once space recovers. A suspend or memory snapshot that fails can also leave it paused: always when the failure says “this computer remains paused because the host is low on disk space”, and other failures can, at any free space. While the platform holds it for disk-space recovery, start, restart, suspend and snapshot answer 503 (“waiting for disk-space recovery”) until it resumes by itself once that recovery can finish, and a stop discards the paused session. Nor does any computer start or resume on a host that is low on disk space: a start, a restart of a stopped computer and the resume that using a suspended computer sets off answer 503 (“this host is low on disk space…”), and a restore answers 503 too, until space on that host has been freed.

osstring

linux or windows.

desktopstring

The display protocol this computer’s desktop speaks: wayland, x11, or absent (X11, or a host too old to say); branch on === "wayland". Screenshots, input and exec behave identically either way. What differs on a Wayland computer is that a window id is the compositor’s address rather than an X window id — opaque and handed back the same way, but not comparable across the two — and that moving or resizing a window the compositor is TILING is refused rather than silently ignored, because a tiled window’s geometry belongs to the layout. Read it here rather than from the template: a computer keeps the image it was built from, and a template’s version can advance underneath it.

templatestring

The template this computer was built from.

cpuinteger

Cores.

ram_mbinteger
disk_gbinteger
running_ram_mbinteger

The guest memory this computer is holding against your plan’s running pool: ram_mb while a process is live, or while a start or resume has been admitted and has not launched yet, and 0 otherwise. Non-zero BEFORE status says running, which is the point of publishing it — admission reserves the memory, and status is read from the guest process, which does not exist yet. So stopped with a non-zero value here is a cold boot on its way up, and suspended with one is a resume on its way up; 0 beside either means nothing has been admitted. Not proof that nothing will be: a start still waiting on this computer’s lifecycle lock has reserved nothing yet. ABSENT rather than 0 wherever this API cannot say — a host that could not be reached, a response written before the computer was read back (a create that answers with a start error, PATCH, either clone), or a host too old to report it. Absence means unknown, never zero.

resolutionstring

The screen this guest renders at, as WIDTHxHEIGHTxDEPTH. This is the coordinate space every click and every screenshot is in — size your model prompts from it rather than by decoding a PNG. Chosen at create and fixed for the life of the computer.

workspace_idstring

The workspace this computer is in. Absent when it is in none. It can change: a stopped computer can be moved to another workspace, or to none, with PUT /computers/{id}/workspace or from the dashboard.

created_atstring

RFC 3339 timestamp.

buildBuild

Present only while status is building or build-failed.

suspendedSuspended

Present only while status is suspended.

idle_suspend_mininteger

How long this computer may go untouched before its host suspends it. Absent on a computer with no override, which follows whatever its host is sweeping at. The host default is deliberately not reported in its place — it is a property of the host and it changes when an operator changes it.

snapshot_scheduleSchedule
browser_proxyBrowserProxy

The proxy this computer’s browsers are sent through — Chromium, Chrome where it is installed, and Firefox — as a managed policy each reads when it starts, locked so the browser’s own settings cannot override it. Only the browsers: exec, a terminal and every other program reach the network directly. Absent when none is set. Set at create or with PATCH /computers/{id}; a clone does not inherit it.

egress_proxyEgressProxy

The proxy ALL of this computer’s outbound TCP is sent through, taken on its host at the computer’s network port, so nothing inside the computer is configured and nothing there can opt out: exec, terminals, package managers and browsers alike. Each connection is tunnelled to the host and port it was going to, by name where the connection says one (the TLS server name, or an HTTP Host) so the proxy resolves it, and otherwise by address. It FAILS CLOSED: when the proxy is down or refuses, the connection fails and nothing is sent directly. UDP to the internet is dropped (so QUIC falls back to TCP, and NTP and other UDP do not work), as is ICMP. DNS lookups still go to the platform’s resolver and are not proxied. Connections through the proxy are closed when the setting changes, and when the computer stops. One owner’s computers on a host hold at most 512 proxied connections at once together (256 through an https:// proxy), and fewer when the host is busy with other owners’; past that a new connection is closed at once. A connection whose server speaks first (SMTP, MySQL, an FTP control channel) waits about 250 ms for the computer to send first, then is tunnelled by address. A connection one side has finished is closed after 60 seconds of silence from the other side. Absent when none is set. Set at create or with PATCH /computers/{id}; a clone does not inherit it.

egress_proxy_pendingboolean

Present and true while a running computer’s egress_proxy names credentials_secret_id and its host does not hold that secret’s value yet — just after a create or a change, after the host restarts, or once the secret is deleted. Every connection the computer opens meanwhile is closed, never sent directly; it usually clears within seconds. It does NOT clear by itself once the computer’s workspace is deleted: its egress credentials then resolve account-wide only, so a proxy naming one of that workspace’s own secrets is held closed until it is saved again naming an account-wide secret. Never present on a computer that is not running.

browser_proxy_pendingboolean

Present and true while a running computer’s guest does not have its browser_proxy yet — just after a create, a start or a change — or still has the files of one just removed. Wait for it to go away before starting a browser that must use the proxy. A browser already running when the setting changes applies it at its next start (Chromium usually within seconds). Never present on a computer that is not running: the setting is applied as it starts.

statestring

Whether this computer exists, as the platform’s own record has it — a different question from status, which is what its host says it is doing. live: its host lists it. unreachable: its host has not answered — on a listing row, for this request; the computer is most likely fine and nothing has been done to it. deleting: a delete was sent and not yet answered. deleted and lost are terminal, and are only ever shown when asked for with state=: deleted is a delete that was answered, lost is a computer whose host was written off by an operator while it was unreachable. Present on listing rows; absent on a single computer, which is served by its host and is therefore live.

deleted_atstring

RFC 3339 timestamp of the answered delete. Present once state has been deleted.

lost_atstring

RFC 3339 timestamp of the write-off. Present once state has been lost.

unreachableboolean

Present and true only on a row the platform served from its own record because the host holding this computer could not be reached. Such a row carries the computer’s identity — name, os, template, size, workspace_id, created_at, state — and nothing only its host knows: no status, no resolution. Its state is unreachable, or deleting when a delete is in flight for it. A listing with any such row carries X-GC-Incomplete, so you only ever see this after asking for a partial answer with allow_partial=1.

secretsobject[]

The secrets bound to this computer — at create, or since with PUT /computers/{id}/secrets — as references: which secret, which revision of it, and the environment variable or file each lands under. Never a value. Present only on a computer that holds secrets; every field in this group is absent on one that does not, except secrets_pending while it is true. On any host that seals saved sessions a computer that holds secrets restarts — by this API or from inside it — and gets its secrets again as it comes back up; a restart of a stopped one is refused (409, reason: "unavailable"): start it instead, which delivers its secrets. It suspends — by hand or when idle — and can be memory-snapshotted. A disk snapshot restored onto it starts it with its secrets delivered afresh; a memory snapshot restored onto it resumes holding what it held when the snapshot was taken, and is refused if it was taken without sealing or the platform will no longer open it (see POST /snapshots/{id}/restore). Taking disk snapshots, cloning, moving between hosts and deletion work as usual, and a clone starts with no secrets bound, unless it is a fork of one of its memory snapshots made with inherit_secrets: true.

secrets_generationinteger

How many delivering starts this computer has had. Each cold start of a computer that holds secrets is one generation; the receipt below names the generation it is for, so a receipt whose generation is behind this one is from an earlier start.

secrets_appliedobject

The receipt that the bound values reached this computer’s desktop session: which start, when, and which revision of each secret. This is the confirmation — a start that was taken but whose delivery did not land has no receipt for its generation and secrets_error says why. Absent before the first delivery.

secrets_errorstring

Why the last delivering start was stopped, in one fixed sentence naming the class of failure and never a value. A start whose delivery fails is stopped by the host and stays stopped until you start it again; this is the reason. Cleared when the next delivering start begins. Absent when the last delivery landed or none has been tried.

secrets_pendingboolean or null

Whether this computer’s desktop is missing a change to its secrets that a restart would deliver: a binding, an env name or file, or a newer value it does not hold — or, once every secret has been removed, whether its running or saved session still holds the values it had. Compared against the receipt, so it clears once any delivery lands — a start, a restart from anywhere, a reboot inside it, or a secret file replaced while it runs — and stays after one that failed. A value replaced for a secret bound as a file is replaced live on a running computer, and is pending only if that did not land. One bound as an environment variable is the same on an image that hands new shells the current values: once they see it, it is not pending, though programs already running — the desktop session among them — keep the value they started with until a restart; on any other image it waits for a restart. A change that removed a secret, or moved one to another revision, stays pending until a restart either way, since the running session may still hold the old value. Only ever true of a running or suspended computer: a suspended one resumes with the values it had, and a stopped one gets everything current when it starts, so it is never pending. null when it could not be checked — the current values could not be read, or the computer’s last delivery predates the record of the names it was made under and it has been rebound since; a restart makes it known. A delivery still on its way is not pending — wait on secrets_delivering for that. Present whenever secrets is, and on a computer with no secrets bound only while true.

secrets_deliveringboolean

Whether values are on their way into this computer’s desktop right now. A computer comes back running a few seconds before its secrets land, and a command run in between sees them unset; after a create, a start or a restart, wait for this to be false before relying on them. Also true from the moment a restart, or a reboot inside the computer, clears them until they are delivered again, and while a replaced value is written to a file. Reported by the computer’s host, which is doing the delivering. False on a computer that is not running and after a delivery that failed (secrets_error says why, and the computer is stopped). After a restart or a reboot it can also go back to false with nothing delivered and no secrets_error: that is a computer that has not come back up three minutes after it, and its secrets are not in it — restart it, or stop and start it. While it reads true only because a restarted computer has not come back up yet, a further restart is accepted, since that is the way out of one that does not; once the delivery itself has begun, a restart answers 409 until it lands. Present whenever secrets is.

vncVnc
operation_idstring

Present only on the answer to a lifecycle call — a create, a clone, or a PATCH that resized — and never on a read: the operation it started, which GET /operations/{id} answers. A clone’s is running until its disk has been copied. Absent, with the call still done, in the rare case the operation could not be recorded; a client must not treat its absence as a failure of the call.

CreditEntry

Fields

idalwaysinteger

Journal sequence number. Entries are returned newest first.

lot_idalwaysstring or null

The lot a credit, expire or debt_settle entry applies to. Always null on debit and clawback entries.

kindalways"credit" | "debit" | "expire" | "clawback" | "debt_settle"
amount_microsalwaysinteger

Nonnegative amount. credit adds a lot; debit and clawback consume credits and may create debt; expire removes a remainder; debt_settle applies a new credit to existing debt. Do not sum kinds without their direction.

debt_microsalwaysinteger

New debt created by this entry, not the running account debt balance.

modelalwaysstring or null

Billed model for a debit, otherwise null.

run_idalwaysstring or null

Funded run identifier for a debit, otherwise null.

stepalwaysinteger or null

Model-call sequence within the run, otherwise null.

created_atalwaysstring

Entry time, RFC 3339 UTC.

CreditGrant

Fields

period_startalwaysstring

Confirmed plan period start, RFC 3339 UTC; calendar month for operator plans.

period_endalwaysstring

Exclusive end of the current grant period, RFC 3339 UTC.

granted_microsalwaysinteger

Actually issued this period, including upgrade differences. Downgrading does not lower this high-water mark.

used_microsalwaysinteger

Issued minus remaining. Includes spending, debt settlement, and removed credits; not solely model usage.

CreditLot

Fields

idalwaysstring

Immutable lot identifier.

kindalways"plan_grant" | "admin_grant" | "topup" | "adjust"
amount_microsalwaysinteger

Original credit amount; one USD is 1,000,000 micros.

remaining_microsalwaysinteger

Unspent amount, before account debt and active reservations.

expires_atalwaysstring

Exclusive expiry instant, RFC 3339 UTC.

created_atalwaysstring

Issue time, RFC 3339 UTC.

EgressProxy

Fields

serveralwaysstring

The proxy, as http://host:port (one that takes CONNECT), https://host:port (the same, spoken to over TLS) or socks5://host:port, lower-cased as stored. The host is a fully qualified name or a public IPv4 address, and the port is explicit; there is nothing else in it — no path, and never a username or password, which is refused because this setting is returned by every read of the computer (name a secret in credentials_secret_id instead). The computer’s HOST connects to it, so private, loopback, link-local (including the cloud metadata address), CGNAT, multicast, reserved and unspecified addresses are refused, and a name is checked again on every connection after it resolves: one that resolves to such an address is not dialled, and the computer’s connection fails. An https:// proxy’s certificate is verified against the host’s public certificate authorities for the proxy’s name (or, for an address, the address); one that does not verify fails every connection, and nothing is sent directly.

credentials_secret_idstring

For an upstream that asks for a username and password: the id of a secret in your store — in the computer’s workspace, or account-wide — whose value is user:password (at most 4096 bytes, no control characters; one trailing newline is ignored). A secret that is not there, or does not hold that, is refused with 400. Three more refusals, each changing nothing: 409 where delivering secrets is not switched on for the deployment, 409 when the secret’s value cannot be read on this control plane (replace its value), and 503 when secrets are not enabled at all. The computer never receives the value, and the secret is NOT bound to it: the computer’s HOST holds the value in memory and signs in to the proxy for it, with Proxy-Authorization: Basic on the CONNECT (http://, https://) or SOCKS5 username/password authentication (each part 1 to 255 bytes), and nothing inside the computer can read it. With http:// or socks5:// the credentials cross the network to the proxy in clear text; prefer https:// when naming credentials. Replacing the secret’s value reaches the host within seconds, for connections opened after it; connections already open stay open. Deleting the secret removes the value from the host, and the computer’s connections are then refused until the setting names a secret that exists. While a running computer’s host does not hold the value — just after a create or a change, or after the host restarts — egress_proxy_pending is true and every connection the computer opens is closed; none is ever sent directly or without the credentials. Once a computer’s workspace is deleted, its egress credentials resolve account-wide only, so a proxy naming one of that workspace’s own secrets is held closed (egress_proxy_pending true) until it is saved again naming an account-wide secret.

GuestDirectoryEntry

Fields

namealwaysstring

Exact filename. Preserve its Unicode, spaces and punctuation when constructing a path.

typealways"file" | "directory" | "symlink" | "special" | "unavailable"
size_bytesinteger

Size of a regular file, when known and representable as a safe JSON integer. Absent for other types.

InputContext

Fields

windowsalwaysWindow[]

The windows GET /computers/{id}/windows lists by default — applications, without the desktop’s own panels and wallpaper — read once, straight after the action. There is no settle wait, so a window still opening when the read ran is not listed yet. [] for an empty desktop.

focusedalwaysWindow or null

The window in windows that has focus, or null when none of them does — including when focus is on the desktop itself, which windows leaves out.

domPageContext

The page on screen, when focused is Chromium (window class Chromium) and the page could be read within the 3 seconds ?context=1 has in all. Absent otherwise, with context_error saying why. Chromium is the default browser; Firefox, also installed, has no dom. Read through Chromium’s DevTools port, which it opens on the computer’s loopback interface only, on images built with it. Only the desktop’s own Chromium profile is read: a page is described only when it is the tab with focus, or the one tab on screen the focused window is named after, so a Chromium started with its own profile has no dom.

InputResult

Fields

okalwaysboolean

The action was carried out.

mechanism"physical" | "unicode" | "mixed"

Present on successful type: physical for plain ASCII typed as US-layout key events; unicode for text with no ASCII, typed by GTK Unicode composition; mixed for both, typed in request order by the same guest helper, which pressed the ASCII key positions itself. Confirms dispatch; verify that the target accepted the exact text.

xinteger

Where the pointer is, for cursor_position. Absent on every other action.

yinteger

The same.

knownboolean

Read this BEFORE x and y. QEMU’s tablet is an absolute pointing device: it accepts coordinates and reports none back, so the only position this platform can tell you about is one it put the pointer at itself. false means nothing has — a guest that has just booted, or been restored, has its pointer wherever it left it — and x and y are then 0, which is a corner the pointer is probably not in rather than a reading. Absent on every action but cursor_position.

contextInputContext

The desktop just after the action, when the request asked with ?context=1 and its windows could be read. Absent otherwise.

context_errorstring

What ?context=1 could not read. With no context, it says why the windows could not be listed: the sentence GET /computers/{id}/windows would have answered (a Windows guest, no desktop session, a guest agent not answering), or that the guest did not list its windows within 3 seconds. Beside a context, it says why that context has no dom: no window has focus, the focused window is not Chromium, Chromium is not listening on its debugging port, the focused Chromium is not the desktop’s own profile or no tab of it can be tied to the focused window, its page does not fill its window (a side panel or DevTools docked beside it), the computer’s image predates page elements, or the page was not described within the same 3 seconds. Either way the action itself was carried out — ok is still true — so do not send it again; list the windows or take a screenshot instead.

Move

Fields

computer_idstring

The computer being moved.

statestring

Where it has got to. staging and moving and resizing are live; done, moved, failed and lost are terminal.

The three terminal failures are three different things and the difference is the whole point of them being separate words. failed means nothing happened — the computer is where it was, at the size it was. moved means the computer IS on another host but at its OLD size: the move landed and the resize did not, which is recoverable by resizing it again where it now is. lost means we stopped watching and cannot say; read the computer to find out.

detailstring

A sentence about the state, meant to be shown to a person. Empty while nothing has gone wrong.

liveboolean

It is still running. This is the flag to poll on rather than comparing state to a list.

cpuinteger

Present only when the move is applying a new value for it.

ram_mbinteger

Present only when the move is applying a new value for it.

disk_gbinteger

Present only when the move is applying a new value for it.

started_atstring

RFC 3339 timestamp.

finished_atstring

RFC 3339 timestamp. Absent while live is true.

operation_idstring

Present only on the answer to POST /computers/{id}/move: the move’s operation, which follows this row to its end. GET /operations/{id} is the same wait as polling GET /moves. Absent, with the call still done, in the rare case the operation could not be recorded; a client must not treat its absence as a failure of the call.

Operation

Fields

idalwaysstring

op_ and 24 hex characters: the operation_id a lifecycle call answered with.

kindalways"create" | "clone" | "start" | "stop" | "suspend" | "restart" | "restore" | "resize" | "move" | "delete"

The call that started it. clone is either clone; resize is a PATCH /computers/{id} that resized; move is POST /computers/{id}/move; delete is DELETE /computers/{id}, succeeded once it was accepted (its answer says what was removed and what of a purge is still queued). More kinds may be added — treat one you do not know as opaque rather than as an error.

computer_idalwaysstring or null

The computer it is about — for a create or a clone, the new one. null only for a restore whose computer could not be named when it was recorded.

statealways"pending" | "running" | "succeeded" | "failed"

pending (accepted, nothing of the computer’s has changed yet — a move still getting its target ready, or a call whose Idempotency-Key reserved it and whose answer has not arrived), running, succeeded or failed. The last two are final.

succeeded means the platform finished the step this call answers for: the guest was started, the disk was copied, the move landed at the new size. It does not mean the desktop inside has finished booting.

erroralwaysobject or null
idempotency_keyalwaysstring or null

The Idempotency-Key the call that started it was sent with, and null for a call sent without one. GET /operations?idempotency_key= finds it by this when asked with a credential of the same scope as that call’s, since keys are kept per scope.

created_atalwaysstring

RFC 3339 timestamp of when the call was answered — or, for a call sent with an Idempotency-Key, of when it was received, since it is recorded before it is carried out.

updated_atalwaysstring

RFC 3339 timestamp of the last change of state or error.

finished_atalwaysstring or null

RFC 3339 timestamp, null while it is live. For a clone, when the platform saw the copy end, which can trail the end itself by up to a minute.

PageContext

Fields

urlalwaysstring

The page’s address, at most 2048 characters.

titlealwaysstring

The page’s title, at most 300 characters.

elementsalwaysPageElement[]

The interactive elements visible in the viewport — links, buttons, form fields, summary, elements with a button, link, tab, menu item, checkbox, radio or textbox role, and contenteditable or onclick elements — that are not hidden or covered at their centre, in document order. At most 150. The top document only: nothing inside an iframe or a shadow root. [] when none is visible.

truncatedalwaysboolean

The page had more interactive elements than elements holds.

PageElement

Fields

tagalwaysstring

The element’s lower-case tag name: a, button, input.

rolealwaysstring

Its role attribute, or "".

namealwaysstring

The first of its aria-label, title, placeholder, alt and name, or "". At most 100 characters.

textalwaysstring

What it says: its text, or a field’s value. A password field’s value is never read, and a checkbox or radio has none. At most 100 characters.

hrefstring

A link’s resolved target, at most 2048 characters. Present on links only.

xalwaysinteger

The left edge of the element’s visible part, in screen pixels — the coordinates input actions take. Click at x + width/2, y + height/2. Placed from the Chromium window’s position, with the page filling the window below its toolbar. A page narrowed by a side panel or DevTools docked beside it is refused (no dom, and context_error says why); DevTools docked at the bottom is not detected and moves the page, not these.

yalwaysinteger

The top edge of the visible part, in screen pixels.

widthalwaysinteger

The visible part’s width, in screen pixels; at least 1.

heightalwaysinteger

The visible part’s height, in screen pixels; at least 1.

PlatformSignal

Fields

seqalwaysinteger
cursoralwaysstring

Opaque checkpoint after this event. Never join executions by PID.

atalwaysstring
computeralwaysstring
sourcealwaysstring
typealwaysstring
dataalwaysobject or object

PlatformSignalGap

Fields

cursoralwaysstring
atalwaysstring
typealwaysstring
computeralwaysstring
sourcealwaysstring
dataalwaysobject

PublicUrl

Fields

idalwaysstring

pu- and sixteen hex characters.

urlalwaysstring

The address to open: https://<label>.<base_domain>, on port 443, with no port in it.

hostnamealwaysstring

<label>.<base_domain>.

labelalwaysstring

The name before the domain: 3 to 40 lowercase letters, digits and hyphens, a letter or digit at each end.

portalwaysinteger

The port inside the computer this URL serves. The app must listen on 0.0.0.0, not 127.0.0.1.

accessalways"public" | "private"

public: anyone with the link. private: only a request carrying an API key of this account as Authorization: Bearer (a key confined to a workspace, only for that workspace’s computers), or a browser signed in to this URL through the dashboard’s Open; neither credential reaches the app. See POST /computers/{id}/public-urls.

disabledalwaysboolean

true when the platform has turned this URL off, by its name, its computer or its account; it answers 404 until the platform turns it back on. Deleting it and making it again does not.

created_atalwaysstring

RFC 3339 timestamp.

updated_atalwaysstring

RFC 3339 timestamp of the last change to its port or access.

RetainOutput

Explicit opt-in, at most 512 UTF-8 bytes. Missing/false preserves legacy behavior. True uses defaults.

Fields

max_bytes_per_streaminteger
retention_secondsinteger

RetainedPrefix

Fields

bytesalwaysinteger
sha256alwaysstring
source_offsetalwaysinteger
next_source_offsetalwaysinteger
end_reasonalways"observed_eof" | "byte_limit"

Why this immutable prefix stopped. Neither value proves execution completion.

Schedule

Fields

enabledalwaysboolean

Whether a capture is taken in this window. A computer that is not running is captured only if its disk has changed since its last automatic snapshot; a stopped or suspended computer left unused takes no new automatic snapshot.

houralwaysinteger

0–23, in tz.

minutealwaysinteger

0–59.

tzalwaysstring

IANA zone name. UTC when unset.

Secret

Fields

idalwaysstring

csec- and sixteen hex characters. What a binding names as secret_id.

namealwaysstring

Its name, as sent with leading and trailing spaces trimmed. Up to 60 characters. Unique within its scope, ignoring ASCII case: names that differ only in ASCII case collide (409, reason: "name_taken").

workspace_idalwaysstring or null

The workspace it belongs to, or null for one available account-wide.

revision_idalwaysstring

csr- and twenty-four hex characters. Moves every time the value is replaced; a replace or delete sends back the one it read.

created_atalwaysstring

RFC 3339 timestamp.

updated_atalwaysstring

RFC 3339 timestamp of the last replace, or of the create.

last_used_atalwaysstring or null

When it was last delivered to a computer. null until it has been.

SnapshotPurge

Fields

selectedinteger

Physical copies selected by the confirmation.

confirmedinteger

Selected copies confirmed absent after deletion.

queuedinteger

Selected copies still listed with a deleting intent. An accepted deletion that remains durable or pending has an unknown outcome.

failedinteger

Selected copies whose deletion was refused.

unknowninteger

Selected copies with an unknown outcome, including requests not sent after transport uncertainty.

remaininginteger or null

Selected copies still visible in the final complete inventory; null if that inventory is unavailable.

unselectedinteger or null

New physical copies not selected by this request; null if the final inventory is unavailable. New consent is required to delete them.

completeboolean

All selected copies were confirmed absent in the final complete inventory. Does not promise that later imports cannot appear.

Suspended

Fields

atstring

When the session was saved, RFC 3339. A resumed guest’s clock is set to the current time within a few seconds of the resume, through its guest agent; the desktop panel’s clock can show the saved time for up to a minute after that.

Template

Fields

namestring

The short name of the template, and what a computer reports as its own template. Accepted as template on a create for the templates we publish; one you published is named by its ref.

refstring

The pinned namespace/name@version for this template. Also accepted as template on a create, and the form to use when it matters that you get exactly this template and this version — a short name resolves to whatever the host currently has under it.

labelstring

Human-readable name.

osstring

linux. (windows is not currently offered on any plan.)

desktopstring

The display protocol this template’s desktop speaks: wayland, x11, or absent (X11, or a host too old to say); branch on === "wayland". Screenshots, input and exec are identical either way — they are taken below the guest — but two things differ on a Wayland template. A window id is the compositor’s address rather than an X window id (opaque and handed back the same way, but not comparable across the two), and moving or resizing a window the compositor is TILING is refused rather than silently ignored, because a tiled window’s geometry belongs to the layout. Absent also on a host deployed before this field existed, which is why it is not defaulted to x11 here.

iconstring

A slug naming a mark this console has an asset for, e.g. claude or debian. Cosmetic — nothing here validates it against a list, and an unrecognised value or none just means the template has no particular icon.

cpuinteger

The default this template builds at.

ram_mbinteger
disk_gbinteger

The FLOOR this template builds at, not a default like the two above — a create asking for less is raised to this and charged at it.

UsageComputer

Fields

idalwaysstring

The computer this line is for. The time one of your own templates spends booting its seed for fast launches is billed under the seed’s own id and name, which GET /computers never lists; that line reads gone once the seed is deleted.

namestring

What it was called. The name from a host that still holds it wins.

run_hoursalwaysnumber
vcpu_hoursalwaysnumber
ram_gb_hoursalwaysnumber
goneboolean

Present and true when this computer is no longer on the fleet. It ran during the window and was deleted, which is why it is billed for and not in GET /computers.

UsagePeriod

Fields

startalwaysstring

RFC 3339.

endalwaysstring

RFC 3339.

sourcealwaysstring

subscription when the boundary came from your plan’s own billing period, which is what an invoice is anchored to — usually the renewal date, and the end of the trial while you are in one. subscription-projected in the short gap between a renewal happening and our hearing about it: start is the previous period’s end and is exact, and end is that plus the previous period’s length, which is a duration rather than a billing rule — so it can be a few days out across a short month, and a whole term out if a plan change took effect at that same renewal. Treat a projected end as provisional and never quote it as a renewal date; start is exact and safe to use. calendar-month when there is no billing period to take it from, in which case the period is the current UTC month. Do NOT read calendar-month as “no subscription”: it also covers a subscription whose period start we do not hold, which is possible for a plan billed over anything longer than a month. This API does not distinguish the two, so do not branch on it to decide whether an account has a plan — read from and to for what was measured. New values may be added, so treat this as an open set.

UsageTotals

Fields

run_hoursalwaysnumber

Wall-clock hours computers on this account spent running.

vcpu_hoursalwaysnumber

Those hours weighted by cores.

ram_gb_hoursalwaysnumber

And by memory.

snapshot_gb_hoursalwaysnumber

The integral of snapshot bytes held, in GB-hours.

snapshot_gb_monthsalwaysnumber

The same integral in the unit snapshots are priced in.

disk_gb_hoursalwaysnumber

The integral of provisioned computer disk, running or not — a stopped computer still holds its disk. Kept apart from the snapshot figures on purpose: disks are provisioned at create and released at delete, snapshots come and go under your plan’s retention and your own deletions, and the two have different remedies.

disk_gb_monthsalwaysnumber

The same integral in the unit disk is priced in.

computersUsageComputer[]

The per-computer breakdown, which is what makes a total checkable. ABSENT when the API key is scoped to a workspace: usage is metered per account, so these lines cover the whole account and would name computers outside the key’s scope. The totals are still account-wide either way.

Vnc

Fields

urlstring

A websocket URL that opens this desktop with full control — keyboard and pointer. Credential included, so treat it as one. Absent for a viewer.

The CLIPBOARD bridge is provisioned on some computers and not others, and clipboard below reports that initial configuration. It is not a live guest-health check; read its caveats if the guest has been modified after creation.

GET and PUT /computers/{id}/clipboard are the route to build on if you only want to write it once, because they need nothing of the HARDWARE — no cold boot, and no permission from a browser. They are not universal either, and what they want is different: a Linux guest with a display and the clipboard tool in the image — xclip on X11, wl-clipboard (wl-paste/wl-copy) on Wayland — since they drive the guest’s own desktop session. A computer built from a golden that predates that tool is refused with a 400 that says so, and Windows is refused outright. That is a much smaller set than the socket’s two conditions, and unlike them it is stated in the answer rather than left for you to infer. Where the socket does carry the clipboard the two do not fight over it — the endpoints write the same CLIPBOARD selection the agent then offers onward.

Those endpoints replace what was documented here as a recipe over POST /computers/{id}/exec in the desktop session. Do not go back to it. Public exec runs a LOGIN shell, which sources the guest user's own profile onto the same stdout your command prints to, ahead of it — fine for running a command, and fatal for reading a value, since an echo left in a .profile corrupts the answer and a deliberate one forges it. The clipboard endpoints do not share that stream. The write was worse: an X selection belongs to a live process, so the holder had to outlive the exec and have its output redirected or the call hung to its full timeout_s; the text had to be base64 and quoted or an apostrophe in it ended the shell word; and the result had to be polled for, because being granted a selection is asynchronous and reading back too soon returns the PREVIOUS clipboard. The PUT does all of that, confirms the selection was taken before it answers, and bills once.

view_urlstring

The same socket, watch only. The daemon drops input on it, so a browser cannot type.

The guest’s CLIPBOARD does not reach a watcher either, and that is enforced rather than asked for: the daemon takes the clipboard capability out of the connection as it is negotiated, so a patched client gains nothing by asking. Worth knowing if you embed this — whatever the person using the desktop copies, including a password, is not visible to anyone holding this URL.

tokenstring

The credential inside url, for a client that builds its own noVNC URL. Absent for a viewer.

view_tokenstring

The credential inside view_url, and what a viewer is given in place of token. Read-only against a patched client rather than only against ours, because the filtering is the daemon’s — and that covers the clipboard coming back as well as the input going out. See view_url.

embed_urlstring

Our hosted viewer, watch only — drop it in an <iframe> or the <com-desktop> element to put a live desktop in your own product without building a VNC client.

Prefer the element if you are choosing, and prefer it especially if one frame of yours shows different computers at different times. Both open the same page, but the element REPLACES its frame whenever the computer or the credential changes, where an <iframe> you re-point keeps one document and one session history. Each URL you assign to that frame becomes a history entry of it, and this URL carries a credential. The viewer takes it out of its own URL as soon as it has read it, and a browser is entitled to refuse that. Browsers cap how many history rewrites they will make, and the two caps are not the same shape. Safari allows a hundred every ten seconds, raises an error past that, and counts them across the whole page — so your own pushState calls draw on the allowance the viewer needs. Chrome allows two hundred per frame every ten seconds and refuses without telling your code at all. Either way an entry the browser refused on keeps what it was holding. Your user pressing Back can then land on that entry, naming a desktop you had already replaced or detached, and it reconnects with the permissions that credential was issued with. The element discards the frame instead, so there is no entry IN THAT FRAME to go back to.

That is the whole of what the element gives you, and the rest is yours, so it is worth saying plainly rather than leaving you to find out. It covers the frame it owns; it does not reach your own history. If your page pushed its own entries while a frame was showing one computer, going Back in YOUR app can restore a frame at that URL, and markup or server-rendered state that carries a token in an attribute comes back with it. Keep the credential out of your routes and out of anything you render server-side, and set it from code on the element. A reload is entitled to work, which is the same reason a credential in a link somebody emails is live until the machine restarts.

terminal_urlstring

A websocket that opens an interactive terminal — a PTY in the guest, running a login shell as the desktop user. Binary frames are the terminal’s bytes in both directions; send a text frame {"type":"resize","cols":120,"rows":32} to resize, and you’ll receive {"type":"exit","code":0} when the shell ends. Size it on the way in with &cols=/&rows= (80x24 by default) rather than resizing immediately after connecting. Sessions persist across disconnects: add &session=<name> to reattach to a named session (default main), and its recent output is replayed. Carries the same controlling credential as url, so treat it as one. Absent for a viewer, and on Windows guests.

Each binary input frame may contain up to 1 MiB. The guest holds at most 1 MiB of pending input per session while a busy or stopped program is not reading. If the next frame will not fit, that entire frame and subsequent input from that attachment are rejected: the client receives {"type":"error","reason":"input_overflow","message":"..."} and its attachment closes. The shell stays alive, and previously accepted input remains queued and may still run. Reconnect to inspect the shell before retrying; do not automatically resend input. Reconnecting replays recent output, never input. If the shell ends before pending input reaches its PTY, its normal exit control includes "input_lost":true alongside the actual exit code.

Two refusals are worth handling, and both are a 409 on the upgrade rather than a socket that closes. A computer last started before interactive terminals existed has no channel for one in its hardware and has to be stopped and started to get one — a restart will not do it, since that is a reset of the same QEMU. And unlike the rest of this API a suspended computer is NOT resumed for you here; the refusal carries resume_required: true, so start it and reconnect.

A third is worth retrying rather than handling: 503 with reason: "contention" means another terminal on this computer is part way through its first-time setup, which takes a few seconds and clears itself. It is deliberately not the 502 that says the guest’s terminal broker could not be reached at all — that one is worth reporting, and retrying it hard will not help.

A second attach to one session detaches the first, and the displaced connection is TOLD: it receives {"type":"detached","reason":"..."} and then a websocket close with code 1000, so a client can tell "another connection took this session" from a dropped network. Reconnecting reattaches and displaces whoever took it, which is the mechanism for reclaiming a session from a half-dead connection you cannot close.

At most 8 sessions exist on one computer, detached ones included, since a session lasts until its shell exits. A connection to a new session name past that is refused with a 409 on the upgrade, with no reason: waiting does not free a session. Type exit in one of them to free its slot. Reattaching to a session name that already exists is never refused for this. A new name that loses a race for the last slot can still be answered after the upgrade, with {"type":"error","message":"too many terminal sessions on this computer"} and a close.

Separately, at most 16 connections can be attached to one computer at once. A connection to a new session name past that is refused with a 409 on the upgrade, with no reason: retrying does not help until one of those connections closes. Reattaching to a session name that is already attached still displaces it as described above, so it is never refused for this. A connection being closed — displaced, or after its session ended — counts toward the 16 until it has closed, and when a new connection needs its room it is closed at once, without its detached, exit or error message.

A refusal that comes with a JSON body, such as the 409s and 503 above, also carries a server-generated X-Request-ID header, repeated in the body as request_id.

events_urlstring

Use this exact URL, including its origin and embedded controlling desktop credential, in a WebSocket client. A REST Bearer API key does not authenticate this socket; Bearer-only requests receive JSON 400 guidance naming events_url, equally for unknown IDs. Watch-only credentials cannot open it.

A websocket that streams what this computer does, without being asked — so an agent can wait for something to happen instead of paying for a screenshot to find out that nothing has. Text frames of JSON, one event each; nothing is ever sent to it.

The first frame is {"type":"hello", ...} and lists the event types this computer can emit — not everything the platform knows how to emit. A guest with nowhere to run a watcher (a Linux one whose hardware carries no terminal channel) never produces the guest-reported half, and events says so rather than leaving you waiting for something that cannot arrive. The two halves of that are not the same requirement: file.changed needs only the terminal channel, so it is available on every Linux computer that has one — including images too old to carry the X bindings, which emit no window events at all. A guest that turns out not to be able to run one — an image built without the X bindings the watcher needs — is not always known at that first frame, and when the answer arrives afterwards a {"type":"capabilities", "events": [...], "detail": "..."} frame revises the list. It goes both ways: a computer stopped and started under an open socket can ACQUIRE the channel its watcher runs over, and the same frame says so. Treat it as replacing what hello advertised: the rest of the stream carries on. Ignore any frame whose type you do not recognise; the vocabulary grows.

Nominate a tree with &watch= and hello carries watching: the paths this stream will report file changes under, as this host has NORMALISED them. A trailing slash and a . segment are accepted and cleaned away, and the cleaned form is what every file.changed carries in watch — so match on what hello gives back rather than on what you sent. The field is absent when you nominated nothing, which means no file.changed can arrive at all; a path this host cannot honour is a 400 on the upgrade rather than a socket that opens and then says nothing.

Each entry is {path, armed} — path as this host normalised it, and armed for whether that tree is ALREADY being watched. "armed": false means not live yet: wait for its {watch, armed: true} before reading silence as "nothing has changed". "armed": true means live now, and no event is coming to tell you so — somebody else nominated it first, the guest answers a nomination once, and this field is how you are told. The same split as ready above: state in hello, transitions on the stream.

At most 8 of these streams can be open on one computer at once, and one account can hold at most 128 across its computers that share a server. A connection past either is refused with a 409 on the upgrade, with no reason and a message naming the limit: the newest is refused and no open stream is closed to make room, so waiting does not help until one of yours closes.

A computer watches at most 32 distinct trees across every stream open on it. A nomination that would take it past that is a 409 carrying reason: "unavailable" and naming the limit; close another stream, or nominate paths it is already watching, which cost nothing extra.

Connect without &since= and hello also carries windows — the desktop as this host last saw it, each entry the same shape GET /computers/{id}/windows returns. It is what you are joining rather than what has just happened, so a client attached to a machine somebody else is already using starts with the screen it is looking at, and every window.closed after it names something you have been told about. The field is present and empty when nothing is open, and ABSENT when you resume from a cursor that could be honoured, because you already hold those windows — absent and empty are different answers, so test for the field rather than for its length. A gap counts as no continuity, so a gapped reconnect carries it too.

It rides in hello rather than arriving as a frame after it so that it cannot come apart from the cursor that implies it: a client which stored the cursor and dropped before a second frame would resume with continuity it had, over a desktop it did not.

Last SEEN, not guaranteed live. A window whose close happened while this host had lost its link to the guest is reported into a dead pipe and stays in this picture, so an entry here can name a window that is already gone. GET /computers/{id}/windows asks the machine and is the authority on the present; this is what makes a later window.closed correlatable, and what to reconcile against that listing if it matters.

hello also carries ready, and it is the difference between waiting and waiting forever. computer.ready is announced once per desktop SESSION, so if the desktop was already up when you attached — somebody else got there first, or you reconnected — the event has happened for that session and will not happen again for it. "ready": true means it already has; wait for the event only when it is false.

Per session, not per running period, and the difference is a real one: restarting the display manager inside a guest destroys the desktop and brings up a new one without the computer ever leaving running. That is a new session, so computer.ready fires again and the windows you were told about belong to the desktop that is gone. Treat a second computer.ready as what it is — a desktop you have not seen before — rather than as a duplicate to discard.

Mind the ORDER when you do, because the replacement announces itself before it says it is ready: the new desktop’s windows arrive as window.opened FIRST, and computer.ready comes after them. The windows of the desktop that is gone are not closed one by one — there is nothing left to report their closes — so a client that empties its map when the second computer.ready arrives throws away the openings it was just handed, and is left describing an empty screen that has windows on it.

The stream does not mark where the replacement begins. Nothing between the last event of the old desktop and the first window.opened of the new one distinguishes them, so there is no rule over the openings you have already received that separates the two — an opening a moment before the replacement and an opening a moment after it look the same on the wire. Do not try to sort them. When a second computer.ready tells you the desktop was replaced, ask GET /computers/{id}/windows: it asks the machine, and it is the authority on the present here as everywhere else on this stream. Keep serving your existing map until the answer comes back — it is stale in a known way, which is better than empty.

After that each event carries type, at, computer, seq, cursor, a source of daemon or guest, and a data object whose shape is the type’s own:

- window.opened — a window EXISTS, which is not always the same as it having just appeared: a watcher starting on a desktop announces what is already open, so the first client to connect after a computer boots is told about the panel and the desktop as openings. What is already there when YOU attach comes in hello’s windows instead. The listing stays the authority on the present — it asks the guest — and this stream is the authority on changes. - window.opened / window.focused — data is a window, the same shape GET /computers/{id}/windows returns, with the same fields, and the same meanings for all but one. Its position and size are as they were at that event: moving or resizing a window does not itself produce an event, so read the listing if you need a window’s geometry right now. The exception is type, which this stream reports more LITERALLY than the listing does — in two ways, both set out in the field’s own description. As everywhere else on this stream, the listing is the authority when the difference matters. - window.blurred — data is {id} and nothing else: the window that HAD the keyboard, reported when focus leaves it and no window at all takes it. Read that literally, because on a desktop that draws its own background — XFCE, which is what the Linux templates run — clicking the background or minimising the last window gives the keyboard to the desktop window itself, and what you get is an ordinary window.focused naming a window whose type is desktop. That is the common case and it is not a blur. A blur is for when the machine genuinely reports no active window, which a bare window manager and some lock screens do. Without it window.focused could only ever move focus and never retract it, so a desktop with nothing focused went on naming whichever window last held it. The window is otherwise unchanged, so it is named rather than described; it is still open and you will still get its window.closed. You are only sent this for a window you were told about, and only when something was focused — a desktop that already had nothing focused sends none. It CAN reach you twice with no window.focused between, and that is not a bug to work around: if the link to the guest drops and comes back, the desktop is re-described to this host rather than replayed to you, so focus that returned while the link was down is not an event you were sent. Treat a blur as the idempotent statement it is — nothing has the keyboard now — rather than as a transition from a window you were told was focused. If you need to be certain which window has it, GET /computers/{id}/windows asks the machine. - window.closed — data is {id} and nothing else. The window is gone, so there is no position or size to report and none is invented; match the id against a window you were told about earlier — hello’s windows, or a window.opened or window.focused this connection was sent. You are only sent closes for those, so an id you do not recognise is not a close whose opening you missed. Gone from the DESKTOP, with one exception that only a desktop of more than 200 windows can reach. This stream describes at most 200 windows plus the one that has the keyboard: a window.focused can therefore name a window you were not told about, and it carries the whole window so that you need nothing earlier to place it. When focus then moves to ANOTHER window outside those 200, you are sent window.closed for the previous one before the window.focused that names the new one — and that window is still on the screen. Below 200 windows a close means what it says; on a desktop that size, read it as "this stream has stopped describing that window". Nothing on this platform will tell you more: GET /computers/{id}/windows refuses a desktop of more than 200 windows rather than enumerate part of it, so the listing cannot settle whether that window is still there, and this reference does not pretend otherwise. Said here because the alternative was worse: a focus onto a window outside the picture used to be dropped, so on a desktop that size every subscriber was told the keyboard had stayed wherever it was when the machine was first watched. - process.exited — data is {execution_id, pid, exit_code} for an observed exit of a command started with background: true. Match execution_id to the ID returned in the 202 response: a PID can be reused, but the execution ID never resolves to a replacement command. Read daemon-only metadata at GET /computers/{id}/executions/{executionId} and independent guest output at GET /computers/{id}/executions/{executionId}/output?stdout_offset=0&stderr_offset=0. Each reader owns its offsets; neither route automatically resumes the computer. The legacy GET /computers/{id}/exec/{pid} consumes shared output and can address a newer command after PID reuse. The lost variant is {execution_id, pid, lost: true}, with no exit_code: the guest agent no longer knows about the command, as can happen after a guest restart. This host cannot say whether it finished, so no outcome is invented. Metadata may briefly report lost before the handle is removed; output is unavailable, and removed or expired IDs return 404. The event tells a waiting caller to stop waiting. Guest output remains volatile and mutable, not suitable for passive Activities/history. This event transport is unchanged: connecting can start a guest watcher/broker even without nominated file watches, so it is not a passive history transport either. - clipboard.changed — data is {selection}, either clipboard or primary. The contents are deliberately not here; read them at GET /computers/{id}/clipboard if you want them. - file.changed — something changed under a directory YOU nominated. This is the one event type that never arrives unasked: pass &watch=<absolute path> when you connect (repeat it for up to four trees) and you are sent changes under those and nothing else. Without it, no file.changed can reach this socket at all. data is {watch, path, kind, dir} — watch is the tree you nominated, path is the absolute path that changed and is always inside it, kind is created, modified or deleted, and dir is present when the thing that changed is a directory. The tree is watched all the way down, and directories created inside it are picked up as they appear. Nothing is announced about what is ALREADY in the tree when you nominate it — those are not changes, and listing the directory yourself is the answer to "what is in there now". A rename inside the tree arrives as a deleted for the old path and a created for the new one rather than as a move: inotify reports the two ends separately and one of them is often outside the tree, so each event is true about the path it names. Writes are coalesced, which is why this is usable at all: an editor save is several kernel events and a compiler writing one object file is more, so changes to one path inside a short window arrive as one event. A file created and then written reads as created; one written and then removed reads as deleted. What you get is the truth about that path when the window closed, not a transcript of every write. Wait for {watch, armed: true} before you act on silence. Arming is not instant: the nomination is accepted the moment you connect, but the guest has to be asked, and on a computer nobody has opened a terminal on this host has to install the watcher into the guest first — seconds, not milliseconds. inotify reports changes and not state, so anything that happens to the tree before the watch is armed is never reported and never will be. armed is what closes that: until it arrives, silence means "not watching yet". It arrives again after anything that re-arms the watch — a stop and a start, a guest reboot, a broker replaced — and a second one means what the first did: reporting starts HERE, so re-read the tree if what happened during the interruption matters. computer.ready says the same thing about a desktop session. One shape carries no path and no kind, and a lost instead: {watch, lost} says the stream under that tree is incomplete, and treat any non-empty lost as "my picture of this tree is wrong". "flood" means the tree changed faster than the cap allows it to be reported — transient, so re-read the tree and keep listening; a build under a watched path costs you one of these rather than thousands of events. "budget" means the tree is bigger than the directory budget one watch gets, so part of it is not being watched at all — permanent for this watch, and the fix is a narrower path. "unwatchable" means the directory is not there yet, is not a directory, cannot be read, or is a SYMLINK — those are refused rather than followed, because inotify pins whatever the link resolved to when the watch was added and repointing it afterwards produces no event at all, so a followed link would report one tree under another tree’s name; nominate the real path. This reason recovers on its own where it can — nominating the directory a job is about to create is a supported thing to do, and the watch starts by itself when it appears. That recovery is announced by {watch, armed: true} and by nothing else: there is no synthetic event for the directory’s own creation. Nominate the narrowest tree you can. A whole home directory under a build is thousands of changes a second, and the replay history this stream keeps is per COMPUTER and shared with every other subscriber to it — so a broad watch spends the history that a client resuming with &since= needs. The cap above is what stops that being unbounded, but a watch that is permanently flooding tells you very little either way. - computer.ready — the guest’s desktop session is up and accepting input. This is the one to wait for after creating a computer, in place of screenshotting until something appears. - computer.idle — nobody has touched this machine for its whole idle window. data carries idle_seconds. Listening is not using: holding this socket open is not activity, or the event could never fire — and it does not hold off an automatic suspend either, so on a computer configured to suspend when idle expect computer.suspended to follow. Setting idle_suspend_min: 0 stops the suspend without stopping the event, subject to the plan’s never-suspend limit. - computer.started / computer.stopped / computer.suspended — data carries status, and previous where there was one. It is absent on the first transition a host reports for a computer after the daemon restarts, which has no earlier status to have moved from — so read it as optional rather than assuming a string is always there.

Reconnecting. Every event has an opaque cursor, and so does the opening hello — that one is where the stream is at the moment you attach, to store if you disconnect before seeing an event. Pass the last one you have as &since=<cursor> and you get what you missed. If this host can no longer replay that far you get a {"type":"gap"} frame instead, naming the oldest cursor it still holds — which is your signal to reconcile with a listing rather than assume nothing happened. It carries no seq: a gap is a statement about the stream rather than a position in it, so a client that skips anything not newer than the last sequence it saw must not skip this. A cursor from before a daemon restart always reads as a gap, which is correct: the numbering it belongs to is gone.

When this host ends the stream it says so first, with {"type":"closed", "detail": "..."}, and the sentence is the difference between a socket worth reopening at once and one that is not. A subscriber that stopped reading for long enough is put down deliberately rather than quietly skipped — dropping events into the floor would be a second, silent way to miss one — so reconnect with the last cursor you hold and you get what you missed, or an honest gap. A computer this host no longer holds says so instead, and that one is not worth retrying against the same place. A socket that simply dies carries no such frame, which is how you tell a network from a decision.

source is worth reading. daemon means this platform observed it. guest means the machine reported it about itself — every window.* event, clipboard.changed, file.changed and computer.ready — and anyone with root inside the guest can make those say anything. They are your machine describing itself, which is exactly as much as they are worth.

Carries the same controlling credential as url; absent for a viewer, because a window title is content and a watch-only credential must not read it. Absent on Windows guests. A suspended computer is refused with 409 and resume_required: true, and a stopped one with a 409 carrying reason: "unavailable".

clipboardboolean

Whether this socket was provisioned with the platform-controlled parts of the guest clipboard bridge: the QEMU channel and an original golden verified to ship spice-vdagent. This is not current availability. A root user can install, remove, disable, or stop the agent later and this value will not change. Treat it as stale after modifying the guest; use the unconditional clipboard endpoints or your own guest-health check then. false for a viewer whatever the computer can do, because a watch-only connection has the clipboard taken out of it as it is negotiated (see view_url).

Even on an unmodified guest provisioned with both halves, a paste is not guaranteed to land. The guest PULLS the selection rather than being pushed it, so the first paste after a connection is often dropped — the agent in the guest may not own the selection yet; send it again. And reading the guest’s clipboard back into the browser is navigator.clipboard.writeText, which needs focus and permission, needs a user gesture in Safari, and does nothing in a cross-origin frame unless clipboard-write was delegated to it. GET/PUT /computers/{id}/clipboard need none of that.

When it is false, what to do about it depends on which half is missing, and both halves are needed. The channel is hardware and is acquired on a COLD start: stop the computer and start it again, or start one that is already stopped. Restarting a RUNNING computer will not do it — that resets the guest rather than rebuilding the machine QEMU was given — and a computer that comes back from a suspend or a snapshot keeps whatever the capture had, so it may lose the channel and need a stop and a start to get it back. The other platform-controlled half is spice-vdagent inside the original image: only images whose capability metadata matches their immutable content digest read true; a same-name build with different bytes does not. The computer keeps the image it was created from, and there is no operation that moves an existing computer onto a newer one. Installing the package yourself can make the bridge work, but does not change this provisioning signal. Windows guests never have it, whatever the hardware says. An unverified image reads false even where the agent is present; the clipboard endpoints are the answer there.

Window

Fields

idalwaysstring

The window id, for POST /computers/{id}/windows/{window}.

titlealwaysstring
classalwaysstring
typealwaysstring

The window type. On an X11 desktop this is the EWMH _NET_WM_WINDOW_TYPE, lower-cased with the _NET_WM_WINDOW_TYPE_ prefix dropped: normal, dialog, dock, desktop. A window that declares only a VENDOR type is reported as that atom, lower-cased and prefix intact, so do not assume the value comes from that list. It is EMPTY when the window declares no type at all, which is legal — EWMH treats an untyped managed window as normal, and this reports what the window said rather than filling it in. The listing has one exception to that, below.

On a WAYLAND desktop it is normal for every window, and nothing below applies. An EWMH window type is an X11 protocol; a Wayland toplevel does not declare one, so there is nothing to read and nothing is inferred from anything else.

type is also the field where GET /computers/{id}/windows and the event stream can report a window differently. Two ways, and the same shape both times: the LISTING interprets what it reads, the STREAM reports what the window literally declared. Both are X11 only:

1. The listing reports dialog for an untyped window that names another as its parent (WM_TRANSIENT_FOR) — a toolkit too old to set a type still marks its modals that way, and “which of these is the dialog in front” is one of the questions the listing exists to answer. The guest watcher does not read that property, so the same legacy modal is dialog in the listing and empty on the stream. 2. A window may declare SEVERAL types — a vendor one first, a standard one after it as the fallback. The listing walks that list and reports the standard atom; the watcher forwards only the first. So a window typed _COMPIZ_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_NORMAL is normal in the listing and _compiz_window_type on the stream.

This applies to everything the watcher describes: window.opened, window.focused, and the windows that hello carries, which are one picture. The other fields of this object mean the same thing on both paths, so a difference in one of THOSE is only ever a difference in when each was read. If the type matters to you, ask the listing — it is the authority on the present here as it is elsewhere.

pidinteger

The process inside the guest that owns this window, where the window says so. ABSENT rather than 0 when it does not: a guest is free to advertise _NET_WM_PID 0, and reporting that as “no pid” would be inventing an answer.

It does not identify the window. An application that keeps one process for several windows — xfce4-terminal is one — reports the same pid on all of them, so killing this pid can take windows you never asked about.

xalwaysinteger
yalwaysinteger
widthalwaysinteger
heightalwaysinteger
focusedalwaysboolean
visiblealwaysboolean

False for a minimised window, and this is the only way to tell one from a window on the screen. A minimised window stays on the list and keeps the coordinates it had, so an agent that clicks at them is clicking at whatever is actually there. Nothing else in the row says so — the title, the class, the geometry and the focused flag all read as ordinary.

Workspace

Fields

idalwaysstring

wsp- and twelve hex characters. What a computer’s workspace_id names.

namealwaysstring

Its name, unique within the account. Up to 40 characters.

created_atalwaysstring

RFC 3339 timestamp.

Types · API reference