Solari

API reference

The raw HTTP surface: every endpoint, every status code, every body shape, callable with curl or any HTTP client. No SDK required and no SDK code on these pages. If you work in TypeScript, Python, Go, Rust or C++, the SDK reference documents the client libraries that wrap these routes.

The SDK is not a 1:1 mirror of the wire

The clients rename fields, resolve presigned URLs for you, and synthesize a few values that never appear on the wire at all. Where that matters, these pages say so. Treat this reference as the contract; treat the SDK as one convenient consumer of it.

Contents

Base URLs

The browser product and the VM/sandbox product are separate gateways on separate hosts. One API key works on both.

SurfaceBase URLReference
Browsershttps://api.getsolari.comBrowser API
VMs & sandboxeshttps://api.getsolari.comVM API, Sandbox API

Request and response bodies are JSON unless stated otherwise. Set Content-Type: application/json on any request that carries a body. The two file data-path routes on the VM gateway are the exception: they move raw application/octet-stream bytes.

Authentication

Every route takes your console API key as a bearer token. The key format is slr_live_<id>_<secret>.

GET /profiles HTTP/1.1
Host: api.getsolari.com
Authorization: Bearer slr_live_4b91ac2f_s3cr3tvalue

Create keys in the console. A key is shown once, at creation. There are four exceptions to the bearer rule:

SurfaceCredential
GET /health, GET /healthzNone. Unauthenticated on both gateways.
/ws/…, /cdp/…, /control/…, /stream/…The signed session ID in the URL is the capability. Treat these URLs as secrets. (The VM control channel additionally takes a bearer upgrade header.)
/files/download, /files/uploadThe signed token in the query string. No Authorization header, since these URLs are handed to browsers and plain curl.
Key verification is cached for 60s

The browser gateway caches verified bearers for 60 seconds, so a revoked key can keep authorizing for up to that long unless the control plane invalidates it first.

Errors

Body shape

Both gateways return a JSON object with a human-readable error. Some handlers add a machine-readable code; branch on the status or on code, never on the prose.

FieldTypeDescription
errorstringHuman-readable message. Not stable across releases, so do not parse it.
codestringMachine-readable discriminator, when the handler sets one. See code values.
detailstringBrowser gateway only. Upstream diagnostic, truncated to 512 characters. Prose. Do not branch on it.
messagestringVM gateway only. Alternate message field some clients read.
retryablebooleanVM gateway only. true marks a transient failure that an idempotent client should retry with backoff.
{
  "code": "ConcurrencyLimitExceeded",
  "error": "Concurrent session limit reached (20)",
  "plan": "starter",
  "cap": 20
}

Individual errors carry extra context fields alongside these: plan, cap, feature, supported, profileId. They are documented on the endpoints that emit them.

Code values

codeStatusGatewayMeaning
FeatureRequiresPlan402BothThe plan lacks a requested feature: stealth, proxy, captcha, desktops, custom templates.
ConcurrencyLimitExceeded429BothThe org is at its concurrent-session cap. Not retryable.
PlanLimitExceeded403BrowserProfile cap reached. Generated by the control plane and forwarded verbatim.
ConcurrencyCheckUnavailable503BrowserThe concurrency store is wedged. The gateway fails closed rather than let an org blow past its cap. Retryable.
InvalidSessionId404BrowserThe session ID was malformed, forged, or another org's. Nothing was released. Do not treat as success.
NotEntitled403VMThe plan does not include desktops or sandboxes.
InsufficientCredit402VMAdmission rejected on prepaid balance.
TemplateKindMismatch400VMThe custom template's kind does not match the requested kind.
TemplateNotReady409VMThe template exists but is still building, or failed.
TemplateBuilding409VMA template cannot be deleted while its build is running.
SnapshotHasChildren409VMLive VMs still descend from this snapshot.
LocalFilesUnsupported400VMcompiled.localFiles is not implemented. Fetch files inside the build instead.
RecordingRequiresDesktop400VMrecord: true on a headless kind: "sandbox". There is no display to capture.
RecordingRequiresGoldenBoot400VMrecord: true combined with fromSnapshot or a tpl_… template. The host cannot wire recording on the restore path.
BrowserUnhealthy is not a wire code

The TypeScript SDK's error union names BrowserUnhealthy, but no gateway response ever carries it. The client synthesizes it when its own post-connect probe fails. Do not write an HTTP handler expecting it.

Retries and status conventions

StatusRetry?Convention
2xxN/ASuccess. 201 on create, 202 on an async template build, 204 on browser release, 200 elsewhere, including a VM exec whose command exited non-zero.
400, 402, 403, 404, 409, 413NoDeterministic. The same request will fail the same way. Fix the request, the plan, or the resource state.
429NoYou are at your concurrency cap. Retrying does not help. Pause or kill a session first. Neither gateway's clients retry this.
501NoThe feature is not configured on this deployment (volumes, port preview). Not transient.
502, 503, 504YesTransient. An upstream was unreachable or capacity was unavailable. Retry with exponential backoff and jitter.

On the VM gateway, a transient failure additionally carries "retryable": true in the body, which is the signal to prefer over the bare status. Send Idempotency-Key: <uuid-v4> on create routes to make that retry safe. GET and DELETE are idempotent by nature: deleting an already-gone VM still returns 200 {"ok": true}.

503 is not always capacity

On the browser gateway 503 also means the concurrency store is down (ConcurrencyCheckUnavailable) or the control plane is unconfigured. On the VM gateway it also covers an unreachable admission service, since a create must fail closed rather than slip past the balance gate. All three are still worth retrying.

Sections

ReferenceBase URLCovers
Browser APIapi.getsolari.comSessions, profiles, proxy countries, replays, health, and the Playwright/CDP/observer WebSocket upgrades.
VM APIapi.getsolari.comThe GUI desktop surface: create, status, kill, pause, resume, and the RFB stream channel.
Sandbox APIapi.getsolari.comThe unified VM surface plus files, volumes, snapshots, templates, and the control WebSocket that carries commands, files, code execution and git.
Live sessions are driven over WebSockets, not REST

REST covers lifecycle only. A browser session is driven over the Playwright wire protocol or raw CDP; a VM session's commands, files, code.run and git ride the control WebSocket. Both are documented in their sections, but neither is expressible as a REST call.

End-to-end example

Create a sandbox, run a command on it over the warm HTTP fast path, then kill it. URL-encode the session ID before putting it in a path; it contains : and ..

export SOLARI_API_KEY=slr_live_…

# 1. Create. The Idempotency-Key makes a retry safe.
SANDBOX=$(curl -sS -X POST https://api.getsolari.com/sandboxes \
  -H "Authorization: Bearer $SOLARI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"template":"base","cpu":2,"memMb":2048}')

ID=$(echo "$SANDBOX" | jq -r .sandboxId)
ENC=$(jq -rn --arg v "$ID" '$v|@uri')

# 2. Run one command. No shell: cmd is the binary, args its argv tail.
curl -sS -X POST "https://api.getsolari.com/sandboxes/$ENC/exec" \
  -H "Authorization: Bearer $SOLARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cmd":"python3","args":["--version"]}' | jq .
# => { "exitCode": 0, "stdout": "Python 3.11.2\n", "stderr": "" }

# 3. Kill it. Idempotent: a second DELETE also returns {"ok": true}.
curl -sS -X DELETE "https://api.getsolari.com/sandboxes/$ENC" \
  -H "Authorization: Bearer $SOLARI_API_KEY"

Health probes

On the shared api.getsolari.com host, the public GET /health is answered by the browser gateway and reports pool capacity (documented in full as GET /health). It is unauthenticated. The VM gateway's bare { "ok": true } liveness probes run behind the load balancer and are not exposed at this hostname: GET /healthz returns 401 here.

GET/health

Unauthenticated pool-health probe served by the browser gateway. Reports idle/busy pool counts and saturation. See the browser reference for the full body.

Example request

curl -s https://api.getsolari.com/health

Example response

{ "ok": true, "idle": 68, "busy": 2, "recycling": 0, "pools": 6, "saturated": false }

GET/healthz

The VM gateway's container liveness probe. Not exposed on the public api.getsolari.com host, where it returns 401.

Example request

curl -s https://api.getsolari.com/healthz
# {"error":"Unauthorized"}

For error semantics in the SDKs rather than on the wire, see Errors.