apps/web/server/index.ts) is an Express app wrapped in a raw http.Server so it can also handle the WebSocket upgrade for the Gateway proxy. Every JSON route lives under /api/, is registered in one router (apps/web/server/api/index.ts), and returns the standard { error: string } envelope on failure. This page covers the cross-cutting facts: base URL, the proxy, auth, body limits, the error shape, and the streaming endpoints, then links to the per-resource reference pages.
These docs describe Clawboo v0.3.1, the current release.
Base URL
The server picks its port at boot, so there is no single hardcoded URL. The default is18790; if it is taken the server scans upward through 18809 (20 consecutive ports) and binds the first free one.
CLAWBOO_API_PORT=Npins the port exactly (no fallback; the server throws ifNis taken).CLAWBOO_API_PORT_START=Mchanges where the auto-scan begins (default18790).- In a non-
--dev(production / CLI) boot with neither Clawboo override set, the legacyPORTenv var is honored (Heroku/Render/Cloud Run compatibility).
<clawboo-home>/api-port.txt so the CLI, the Vite dev proxy, and e2e helpers can discover it without scanning. All curl examples in the per-resource pages assume the default 18790.
Host binding
The server binds loopback (127.0.0.1) by default; a fresh install is never reachable by other hosts. Set HOST to widen the bind (e.g. a headless/remote box); HOSTNAME is deliberately ignored. Binding a non-loopback interface without an access token makes the server refuse to start (a SECURITY: error, exit 1), so pair a wide bind with STUDIO_ACCESS_TOKEN, or opt in explicitly with CLAWBOO_ALLOW_INSECURE=1.
How the SPA reaches /api
The SPA and the API are served from the same origin in production: the Express app serves the Vite build (dist/ui/) as static files with a GET catch-all that returns index.html for client-side routes, and the same app mounts the API router. So a browser at http://localhost:18790 hits /api/... on that same origin with no CORS.
In dev (pnpm dev), Vite serves the SPA on :5173 and proxies /api to the dynamic API port; CORS (origin: true, credentials: true) is enabled only in dev mode.
Request body limit
All routes share one JSON body parser, applied before the router:The access gate
Authentication is opt-in via theSTUDIO_ACCESS_TOKEN env var. When it is unset or blank, the gate is disabled and every route is open; the secure-by-default posture relies on the loopback bind, not on a token.
When the token is set, the gate (packages/gateway-proxy/src/access-gate.ts) is enforced as middleware before the router:
The
/api/ prefix check is case-folded (pathname.toLowerCase()), so /API/settings, /Api/..., etc. are gated identically; there is no case-sensitivity bypass (the server also sets Express case sensitive routing: true). The configured token is validated against a safe charset (^[A-Za-z0-9._~-]+$) when the gate is built; a token containing a disallowed character disables the gate with a loud warning (fail-loud, never a silent lockout).access_token) and cookie name (clawboo_access) are the defaults; both are configurable in createAccessGate(...) but the server passes neither, so the defaults apply.
The WebSocket upgrade: /api/gateway/ws
The one non-HTTP endpoint. The raw http.Server routes the upgrade event: a request whose pathname is exactly /api/gateway/ws is handed to the Gateway proxy; every other upgrade has its socket destroyed (Vite HMR runs on its own port). The proxy opens an upstream WebSocket to the OpenClaw Gateway, injects the server-side auth token + device signature into the connect frame, and forwards frames bidirectionally; the browser never sees the upstream token.
This is a protocol stream, not a request/response endpoint. The frame shapes and lifecycle are documented in the Gateway & events concept page, not here.
The access gate’s
allowUpgrade(req) check runs before the proxy forwards the upgrade. If STUDIO_ACCESS_TOKEN is set, a browser must already hold the clawboo_access cookie or the upgrade is refused (socket destroyed).The error envelope
Every JSON route returns the same envelope on failure:error string is the human-readable message. Common statuses across the surface:
A handful of routes deviate from the bare
{ error } shape, and those deviations are documented on the resource page:
- The native
/api/runtimes/:id/healthcheckroute returns{ ok: false, error }(success is{ ok: true }). /api/runtimes/:id/runreturns{ ok: false, reason }on its non-200 board/runtime refusals.- SSE routes emit
error-typed event frames inside the stream rather than an HTTP error body (see below).
Streaming endpoints (SSE)
Eight routes stream a Clawboo-shaped Server-Sent Events response, not request/response. (The four MCP session channels atGET /api/mcp/* are SSE too, but they carry MCP JSON-RPC rather than a Clawboo
event catalog; see Tools & MCP.) They set Content-Type: text/event-stream, flush headers, and emit data: <json>\n\n frames (the three live-tail routes prefix each persisted row with an id: cursor line, and the two chat streams add named event: frames on top). They are documented with an event-stream catalog (event type → payload) on their resource pages, not with a response body:
Route index
148 routes across 13 resource groups: 64 GET · 60 POST · 11 DELETE · 8 PATCH · 5 PUT. Each group has a dedicated reference page.The route counts above sum to 148, matching the router exactly; every registered route belongs to one resource page. The “Misc” page is the catch-all for the smaller UI-backing resources that do not warrant their own page.
See also
- Settings & health API, the one liveness surface that works with the Gateway down
- System API, OpenClaw install/configure/gateway lifecycle (SSE), plus Clawboo’s own self-version and self-update
- Runtimes API, the gold-standard per-route reference for this group
- Gateway & events, the
/api/gateway/wsproxy + the Bridge→Policy→Handler pipeline - Security, access gate, device auth, redaction, exposing safely
- Environment variables,
STUDIO_ACCESS_TOKEN,CLAWBOO_API_PORT*,HOST