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 HTTP 200 with { "status": "XO Cowork API running" }.
  • /api/config/workspace returns a default key present in roots.
  • /api/chat/prompt returns a non-empty stream_id.
  • The SSE request ends with done, or surfaces an actionable agent-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.