pols.so API
Control plane API for pols.so developer sandboxes: full Ubuntu 24.04 VMs with root, systemd and Docker.
This file is the source of truth. The Go server interface, the Go client
(pkg/api) and the TypeScript client (clients/typescript) are generated
from it with make generate.
Authentication: /v1 endpoints require an org-scoped API key as a
bearer token (Authorization: Bearer pols_...), except the public
/v1/openapi.yaml. The CDP WebSocket endpoint also accepts the connection
token described under GET /v1/sandboxes/{sandbox}/browser/cdp.
Lifecycle calls are asynchronous. They record the desired state and return
at once; a reconciler drives the runtime to it. Poll GET /v1/sandboxes/{sandbox} until status equals desired_state (or is
error unless deletion is pending). Conflicting stop/resume requests
return 409 until the current transition finishes.
Wherever a path takes {sandbox} or {template}, either the ID
(sbx_..., tpl_...) or the org-unique name is accepted.
Computer use: every sandbox has a 1920x1080 X11 desktop and Google
Chrome. /computer/* takes screenshots of the whole desktop and drives
its mouse and keyboard; coordinates are pixels in that 1920x1080 space,
with (0, 0) at the top left. /browser/cdp connects Playwright or
Puppeteer to the sandbox’s Chrome over the Chrome DevTools Protocol,
proxied by the control plane; Chrome’s debugging port itself only
listens inside the sandbox.
Edge: the edge serves sandbox content on its own domain (the sandbox
domain, on.pols.so in production), never on the API’s or the site’s
domain. A published port is at
https://<sandbox>-<port>.<sandbox domain>, where <sandbox> is the
sandbox ID with _ replaced by -; the desktop is at
<sandbox>-desktop and the CDP endpoint at <sandbox>-cdp. Ports are
private unless published as public: open them with a login link from
POST /v1/sandboxes/{sandbox}/ports/{port}/link, whose token the edge
exchanges for a cookie scoped to that one host. The desktop takes no
cookie: its link from POST /v1/sandboxes/{sandbox}/desktop carries a
token in the URL fragment, which the edge’s desktop page presents when
it connects. ssh <sandbox ID>@<gateway>
reaches a running sandbox with any key registered under /v1/ssh-keys.
Vault: each org has a vault of passwords and environment variables,
encrypted at rest. Its owner adds, replaces and deletes entries on the
website (https://my.pols.so/account); API keys can only list their names
(GET /v1/vault) and name them in secrets when creating or forking a
sandbox, which gets each one as an environment variable of that name.
No endpoint returns a value.
Limits: /v1 calls are rate limited per client address and per org,
sandbox and template lifecycle calls more tightly, and an org may have
only so many exec, file, computer and CDP calls in progress at once.
Over a limit the API answers 429 rate_limited with a Retry-After
header. A request body that stalls is answered with 408 timeout.
Errors and headers: a failure of the sandbox host is answered with 502
runtime_error and a fixed message; the details are only in the
server’s log. A {sandbox} or {template} that is neither a well-formed
ID nor a name is 404 not_found. Names and descriptions must be UTF-8
text without control characters other than tab, and exec arguments
and paths must not contain NUL. Every response carries
X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer,
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
and Cache-Control: no-store, except that /v1/openapi.yaml may be
cached.
https://api.pols.soorg
- GETThe org the API key belongs to, with its quotas and current consumption
/v1/org - GETCPU, memory and disk use summed over the org's running sandboxes
/v1/org/stats
api-keys
- GETList the org's API keys (secrets are never returned)
/v1/api-keys - POSTRefused; create API keys on the website's account page
/v1/api-keys - DELETERefused; revoke API keys on the website's account page
/v1/api-keys/{key}
sandboxes
- GETList sandboxes
/v1/sandboxes - POSTCreate a sandbox from a template and start it
/v1/sandboxes - GETGet a sandbox
/v1/sandboxes/{sandbox} - DELETEDelete a sandbox and its disk
/v1/sandboxes/{sandbox} - GETA sandbox's current CPU, memory and disk use and its recent history
/v1/sandboxes/{sandbox}/stats - POSTStop a sandbox (snapshot, then free; stopped time is not metered)
/v1/sandboxes/{sandbox}/stop - POSTResume a stopped sandbox
/v1/sandboxes/{sandbox}/resume - POSTFork a sandbox into a new running sandbox with a copy of its disk
/v1/sandboxes/{sandbox}/fork
exec
files
- GETRead a file from a running sandbox
/v1/sandboxes/{sandbox}/files - PUTCreate or overwrite a file in a running sandbox
/v1/sandboxes/{sandbox}/files
computer
- GETTake a PNG screenshot of the sandbox's whole 1920x1080 desktop
/v1/sandboxes/{sandbox}/computer/screenshot - POSTClick, double-click, drag, type, press keys or scroll on the sandbox's desktop
/v1/sandboxes/{sandbox}/computer/actions
browser
- GETConnect to the sandbox's Chrome DevTools Protocol endpoint (WebSocket)
/v1/sandboxes/{sandbox}/browser/cdp - POSTGet a short-lived WebSocket URL for the sandbox's Chrome DevTools Protocol endpoint
/v1/sandboxes/{sandbox}/browser/cdp
edge
- GETList the sandbox's published ports
/v1/sandboxes/{sandbox}/ports - PUTPublish a sandbox port over HTTPS, or change its visibility
/v1/sandboxes/{sandbox}/ports/{port} - DELETEStop publishing a port
/v1/sandboxes/{sandbox}/ports/{port} - POSTGet a short-lived login link for a published port
/v1/sandboxes/{sandbox}/ports/{port}/link - POSTGet a link to the sandbox's desktop in the browser (noVNC)
/v1/sandboxes/{sandbox}/desktop - GETHow to reach the sandbox over SSH through the gateway
/v1/sandboxes/{sandbox}/ssh
ssh-keys
- GETList the org's SSH public keys
/v1/ssh-keys - POSTRegister an SSH public key for the org's sandboxes
/v1/ssh-keys - DELETERemove an SSH public key
/v1/ssh-keys/{key}
vault
templates
- GETList system templates and the org's own templates
/v1/templates - POSTSave a sandbox's disk as a named template
/v1/templates - GETGet a template
/v1/templates/{template} - DELETEDelete one of the org's templates
/v1/templates/{template}