Quickstart: Make Your First Cowork API Request
Verify a Cowork API instance, discover its workspace configuration, and stream a first chat turn.
Use this guide to verify that xo-cowork-api is reachable, discover the workspace
instead of hardcoding its paths, and send one streamed chat turn.
Before you start
You need a running XO Coworker workspace. The API runs inside it on port 5002 by
default.
export BASE="http://localhost:5002"Local desktop requests use loopback and do not need an auth header. A remote
workspace proxy can require a host-provided Authorization: Bearer <token> or
Coder-Session-Token: <token> header. Add one only when that workspace host tells
you to: the FastAPI app itself does not validate either header. That workspace
proxy token is different from the upstream swarm token described in the API
overview.
Verify the API process
curl --fail --silent --show-error "$BASE/"A healthy instance returns:
{ "status": "XO Cowork API running" }If this request cannot connect, confirm that the Coworker workspace is running
and that BASE points to its current local port or tunnel URL.
Discover the workspace
curl --fail --silent --show-error "$BASE/api/config/workspace"The response identifies the active backend and its canonical projects root:
{
"roots": {
"openclaw": "/Users/me/xo-projects",
"claude_code": "/Users/me/xo-projects"
},
"default": "openclaw"
}Use roots[default] whenever your client creates or opens a project. Do not
hardcode ~/xo-projects/; the server can override it with
XO_PROJECTS_ROOT.
Reserve a first chat stream
curl --fail --silent --show-error \
-X POST "$BASE/api/chat/prompt" \
-H "Content-Type: application/json" \
-d '{"text":"Reply with exactly: XO Cowork API connected."}'The API returns a stream identifier and a logical session identifier:
{
"stream_id": "8f3a2b1c-...",
"session_id": "9d4e5f6a-..."
}Copy stream_id for the next step. On a new OpenClaw session,
session_id can initially be null; the stream will emit the real id in a
session-created event.
Consume the streamed response
Replace <stream_id> with the value returned above:
curl --no-buffer "$BASE/api/chat/stream/<stream_id>"A normal new-session turn follows this shape:
event: session-created
data: {"session_id":"9d4e5f6a-..."}
event: text-delta
data: {"session_id":"9d4e5f6a-...","text":"XO Cowork API connected."}
event: done
data: {"finish_reason":"stop","session_id":"9d4e5f6a-..."}Accumulate every text-delta, ignore heartbeat content, and stop on done
or agent-error. Calling prompt without opening its stream leaves an
in-memory entry behind, so clients should always consume or explicitly abort a
reserved stream.
The status and workspace-config requests work before you build a chat UI. A chat
turn also requires a configured runtime and model provider. Use the Auth
API for Clerk and Claude/Codex bootstrap flows, and
GET /api/models from the Config API to populate
the model choices your workspace exposes.
Confirm the integration
Before moving on, verify all four outcomes:
/returns HTTP200with{ "status": "XO Cowork API running" }./api/config/workspacereturns adefaultkey present inroots./api/chat/promptreturns a non-emptystream_id.- The SSE request ends with
done, or surfaces an actionableagent-error.
Build the next layer
- Read the Overview for remote auth, content types, and the shared error envelope.
- Use the Chat API for reconnect, heartbeat, abort, and full event semantics.
- Copy the production-oriented TypeScript patterns in Frontend Integration.
- Use the Files API and Sessions API to add project navigation and history.