claw

Protocol

The agent speaks JSON over HTTP plus one WebSocket endpoint. The Go types live in internal/api/types.go. The protocol isn't versioned separately; GET /v1/health reports the agent's build version.

Auth

Every endpoint except /v1/health requires:

Authorization: Bearer <token>

Failures return 401 with {"error":"unauthorized"}. All errors use {"error":"<message>"}.

Endpoints

Method Path
GET /v1/health {"host","os","version"}; no auth
GET /v1/sessions Array of sessions, oldest first
POST /v1/sessions Create a session
DELETE /v1/sessions/{id} Stop and remove a session
GET /v1/sessions/{id}/attach WebSocket upgrade
GET /v1/dirs/recent {"dirs":[...]}: folders sessions were started in, most recent first (at most 20), without folders that no longer exist; [] when the agent keeps no history
GET /v1/external Array of external sessions, oldest first
PUT /v1/external/{id} Announce or renew an external session
DELETE /v1/external/{id} Withdraw an external session
POST /v1/hooks Report a Claude Code hook event (204 applied, 202 ignored, 400 bad JSON)

{id} may be a unique prefix. An ambiguous prefix or unknown ID is an error.

Session

{
  "id": "815856a6",
  "name": "api",
  "dir": "/home/alvaro/src/api",
  "args": ["--resume"],
  "pid": 41233,
  "created": "2026-09-30T10:12:00Z",
  "status": "running",
  "viewers": 1
}

status is running (live, no hook seen yet), working, idle, needs_input (driven by hooks), or exited; gone is reserved for external sessions. An exited session stays exited. origin is managed for sessions the agent spawned. exit_code is present only once exited; args only when non-empty. claude_session_id, transcript and last_event appear after the first hook.

Create request

{ "name": "api", "dir": "~/src/api", "args": ["--resume"], "cols": 120, "rows": 40 }

All fields are optional. dir defaults to the agent user's home and ~ is expanded on the agent. Size defaults to 120×40. There is no field for the executable; that is fixed by the agent's --claude flag.

Hooks

POST /v1/hooks takes {"event","session_id","transcript_path","cwd","message","clawsh_session_id","pid"} (message and clawsh_session_id optional). With a clawsh_session_id matching a managed session, the event sets its status: UserPromptSubmit → working, Stop and SessionStart → idle, Notification → needs_input, SessionEnd → unchanged. Events without a known clawsh_session_id answer 202 and are ignored.

External sessions

Claude sessions the agent doesn't own, announced by clawsh mcp (see Claude Code plugin). They're listed only; you can't attach to them or kill them.

PUT /v1/external/{id} takes {"name","dir","pid"}, all optional; name defaults to the basename of dir. The caller picks the {id}: 1–64 characters of [0-9a-zA-Z_-]. The call is an upsert, and each one renews a lease. The agent forgets entries not renewed within 90 s.

{ "id": "aecd9193be8d78aa", "name": "api", "dir": "/home/alvaro/src/api", "pid": 5120,
  "created": "2026-10-02T02:10:39Z", "seen": "2026-10-02T02:11:09Z" }

Attach WebSocket

type Direction Fields
resize hub → agent cols, rows
exit agent → hub code

On connect the agent sends the scrollback as binary, then live output. When the process ends it sends exit and closes normally. The agent pings every 20 s and drops a client silent for 60 s.

Edit this page on GitHub