Skip to content
pols.so docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

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 and client (pkg/api), the TypeScript client (clients/typescript) and the Python client’s types (clients/python/src/pols/_schema.py) 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.

Roles: an org has users, each an owner, admin or member (see the Role schema), invited by email on the website (https://my.pols.so/account/members). Every user makes their own API keys there, and a key acts with the current role of its user: changing the role changes what the key may do, and removing the user from the org revokes their keys. Every role may use sandboxes, templates, SSH keys and the vault’s names through the API; GET /v1/api-keys lists a member’s own keys only. GET /v1/org says the role of the calling key.

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 until a standby sandbox is stably stopped with status=stopped and desired_state=standby (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’s host_label (a generated name with a random suffix, like braveotterk3f9) or the sandbox ID with _ replaced by -; the desktop is at <sandbox>-desktop and the CDP endpoint at <sandbox>-cdp. Both forms reach the same sandbox with the same login links and tokens. A name given to a sandbox is never part of an address, and the host label never changes, a rename included; no host label is ever given to another sandbox. The VM’s own hostname stays the ID form (sbx-...). An unknown sandbox, a port that is not published and a private port without a valid login link or session all get the same 404 page. 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 by a user who is still an active user of the org.

Standby: a running sandbox that nothing uses for the deployment’s standby period (15 minutes by default) while its CPU and outbound traffic stay low goes to status standby: its VM is frozen with its memory kept, so it uses no CPU. On the standard engine it is billed for the full RAM its size reserves; on Cloud Hypervisor (an operator opt-in per org) its memory is written to disk and billed as disk instead, with no RAM charge (GET /v1/usage). Its disk keeps being billed. Use means a call on its contents (exec, files, computer use, CDP, a desktop link) or SSH and HTTP through the edge. Any of those wakes it, and so do resuming, forking and saving it as a template: the call waits until the sandbox runs again, usually within a second, and answers 503 waking if that takes longer than the deployment allows. Reading it (GET) does not wake it. A sandbox in standby keeps its place among the org’s running sandboxes, so waking it never fails on that quota; its standby time does not count against the monthly sandbox-hours. While billing is off for the org, once those are used up it cannot wake (403 quota_exceeded); once billing is on and its credit is used up, it cannot wake (402 insufficient_credit). Either way it is stopped with the running ones. If its VM stopped meanwhile (a host reboot), it shows status stopped with desired state standby, gives up its place and is billed for nothing; the next use boots it like a resume, with the running quota checked, except a fork or template save, which copies its disk without booting it.

Vault: each org has a vault of passwords and environment variables, encrypted at rest. Its owners and admins add, replace and delete 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.

Webhooks: the org’s owners and admins register HTTPS endpoints on the website (https://my.pols.so/account) and pick the events each one gets: sandbox.ready, sandbox.stopped, sandbox.error and sandbox.expired. API keys can only list endpoints and their delivery log (GET /v1/webhooks and GET /v1/webhooks/{webhook}/deliveries). Each delivery is a POST of a WebhookEvent JSON body with a Pols-Signature: t=<unix seconds>,v1=<hex> header: the HMAC-SHA256, keyed with the endpoint’s secret (whsec_..., shown once when it is created), of the timestamp, a . and the raw body. Check it in constant time against the raw body before parsing it, refuse timestamps more than 5 minutes from your clock, and process each event id once: a delivery is retried until an endpoint answers 2xx within 10 seconds, with backoff for about a day, so the same event can arrive more than once and out of order.

Billing: GET /v1/billing and PUT /v1/billing read and set who the org’s invoices are made out to, as do the billing page on the website (https://my.pols.so/billing); only owners’ and admins’ keys may (403 forbidden otherwise). A business’s EU or Northern Ireland VAT ID is checked against VIES when it is saved. When VIES does not answer, the details are saved anyway with the VAT ID unverified, and it is checked again later. The details and VIES’s answer decide how VAT applies to the org’s top-ups and subscription (TaxTreatment), and each paid top-up and subscription month gets an invoice (GET /v1/invoices), a PDF kept for 10 years. The subscription (/v1/billing/subscription) costs EUR 20 a month and includes EUR 25 of usage each month, of which at most one month’s rolls over into the next.

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.

Version 0.1.0
Base URLhttps://api.pols.so

org

api-keys

sandboxes

timeline

exec

files

computer

browser

edge

ssh-keys

vault

webhooks

billing

templates

usage

system