REST API
Every route takes Authorization: Bearer <key> and JSON, except GET /health, which is open. Bad input answers 400, an unknown session or workspace 404. A daemon also checks the Host header against its allowed hosts.
Sessions and workspaces
Section titled “Sessions and workspaces”| Route | What it does |
|---|---|
GET /api/sessions |
List sessions |
POST /api/sessions |
Create a session |
DELETE /api/sessions/{id} |
Delete a session |
GET /api/workspaces |
List workspaces (?include_archived=true for archived ones too) |
POST /api/workspaces |
Create a workspace |
DELETE /api/workspaces/{id} |
Delete a workspace |
POST /api/workspaces/{workspace_id}/sessions/{session_id} |
Attach a session to a workspace, with a role |
POST /api/projects/resolve |
Find or create the workspace and session of a project |
POST /api/projects/resolve takes a project key (the git remote without scheme, user and .git, such as github.com/acme/checkout), a name and the client. The same key always gets the same workspace and session, so teammates on the same remote share them.
curl -X POST <url>/api/projects/resolve -H "Content-Type: application/json" \ -d '{"key": "github.com/acme/checkout", "name": "checkout", "client": "my-agent"}'# {"workspace_id": "...", "session_id": "...", "created": true}Search
Section titled “Search”POST /api/search runs the same search as the semantic_search tool.
| Field | |
|---|---|
query |
what to find, in words |
scope |
session or workspace (both with id), or global |
mode |
fast, local or accurate; optional |
sources |
records (default), messages or all |
limit |
default 5, at most 50 |
author |
only this author’s records; optional |
curl -X POST <url>/api/search -H "Content-Type: application/json" \ -d '{"query": "why did we pick RocksDB?", "scope": "workspace", "id": "<uuid>", "mode": "fast"}'It answers the hits only, without the neighbouring records MCP adds around them. A host without embeddings answers 503.
Conversation
Section titled “Conversation”POST /api/messages appends turns to a session. It is idempotent on turn_id, so a retry is safe, and redaction applies.
curl -X POST <url>/api/messages -H "Content-Type: application/json" -d '{ "session_id": "<uuid>", "messages": [ {"turn_id": "t-1", "role": "user", "text": "why is the build slow?"}, {"turn_id": "t-2", "role": "assistant", "text": "...", "author": "my-agent", "tools": ["Bash"]} ]}'# {"appended": [0, 1], "skipped": 0, "vectorized": 2}Cards and files
Section titled “Cards and files”| Route | What it does |
|---|---|
GET /api/cards?workspace_id=<id> |
The user card and the workspace’s project card |
PUT /api/cards/user |
Write the user card |
PUT /api/cards/workspace/{id} |
Write a project card |
GET /api/file?workspace_id=<id>&path=<path> |
What the memory knows about a source file |
A card write names the version it was edited from (expected_version). It answers 200 with the new version, or 409 with the latest one if someone changed it meanwhile.
Hook events
Section titled “Hook events”POST /api/events takes a client’s hook event and answers what the host’s modules say, as a Claude Code hook answer to print. This is what the plugin’s hooks call.