<CLAWBOO_HOME>/clawboo.db (default ~/.clawboo/clawboo.db); these routes serve and mutate local state and do not require the Gateway to be up. All POST/PUT bodies are parsed by express.json({ limit: '2mb' }).
The order in
api/index.ts matters: /api/cost-records/summary and /api/exec-settings/all are registered before their shorter prefixes so the two-segment paths are not swallowed.Routes
| Method | Path | Summary | Stream? |
|---|---|---|---|
| GET | /api/cost-records | List cost records (period + agent filter) | No |
| POST | /api/cost-records | Record one run’s token usage; computes USD | No |
| GET | /api/cost-records/summary | 30-day aggregation: totals, per-agent, time series | No |
| GET | /api/chat-history | Load a session’s transcript entries | No |
| POST | /api/chat-history | Batch-insert transcript entries (idempotent) | No |
| DELETE | /api/chat-history | Clear a session’s transcript | No |
| GET | /api/graph-layout | Load saved Ghost Graph node positions | No |
| POST | /api/graph-layout | Upsert Ghost Graph node positions | No |
| GET | /api/personality | Load an agent’s personality slider values | No |
| POST | /api/personality | Upsert an agent’s personality config | No |
| GET | /api/skills | List installed skills (optional agent filter) | No |
| POST | /api/skills | Install a skill (injection scan → 422 on finding) | No |
| DELETE | /api/skills | Remove an agent from a skill (drops the row if last) | No |
| GET | /api/exec-settings | Load an agent’s execution settings | No |
| GET | /api/exec-settings/all | Map of all agents’ execAsk settings | No |
| POST | /api/exec-settings | Upsert an agent’s execution settings | No |
| GET | /api/fleet/summary | Read-only fleet-health aggregation | No |
| GET | /api/boo-zero/team-briefs/:teamId | Load a team’s Boo Zero brief | No |
| PUT | /api/boo-zero/team-briefs/:teamId | Upsert a team’s Boo Zero brief | No |
| DELETE | /api/boo-zero/team-briefs/:teamId | Remove a team’s Boo Zero brief | No |
| GET | /api/boo-zero/global-brief | Load the global Boo Zero brief | No |
| PUT | /api/boo-zero/global-brief | Upsert the global Boo Zero brief | No |
| GET | /api/boo-zero/display-name/:agentId | Load Boo Zero’s display-name override | No |
| PUT | /api/boo-zero/display-name/:agentId | Set Boo Zero’s display-name override | No |
Cost records: /api/cost-records
Token-usage records, one row per accounted run. The POST handler computes USD from a built-in per-model pricing table (calculateCostUsd), and the summary route aggregates the last 30 days for the cost dashboard.
GET /api/cost-records
Lists cost records, newest first, capped at 500.
- Query params:
| Param | Type | Default | Notes |
|---|---|---|---|
period | 'today' | 'week' | 'month' | none (all) | today = midnight today; week = now − 7 days; month = now − 30 days; any other value = no time filter |
agentId | string | none | Filter to one agent |
- Request body: none.
Responses
200 OK: the matching records (a costRecords row array):
500 Internal Server Error: a DB failure:
Example
POST /api/cost-records
Records one run’s token usage. The handler computes costUsd from the model name and token counts, then upserts a placeholder agents row (the cost_records.agentId foreign key requires the agent to exist) before inserting the record.
- Request body:
Pricing is a built-in table keyed by Claude model ids (opus / sonnet / haiku tiers), with a substring fallback and a
default of 15 per million input/output tokens. An unrecognized model is priced at the default rate.Responses
400 Bad Request: body is not an object:
400 Bad Request: a required field is missing (inputTokens / outputTokens are checked for null/undefined, so 0 passes):
200 OK: the record was inserted:
500 Internal Server Error: a DB failure:
Example
GET /api/cost-records/summary
Aggregates the last 30 days of cost records into dashboard totals, a per-agent breakdown (agent name joined from agents), and a 30-day time series with zero-filled empty days. Takes no parameters.
- Path/query params: none.
- Request body: none.
Responses
200 OK: the aggregation:
500 Internal Server Error: a DB failure:
Example
Chat history: /api/chat-history
Persists per-session chat transcripts in the chat_messages table. Each row stores a JSON-serialized TranscriptEntry; reads parse the JSON back, skipping any corrupt row.
GET /api/chat-history
Loads a session’s transcript entries, oldest first.
- Query params:
| Param | Type | Default | Notes |
|---|---|---|---|
sessionKey | string | , | Required; the session to load |
limit | number | 200 | Clamped to a max of 1000; a non-numeric value falls back to 200 |
- Request body: none.
Responses
400 Bad Request: missing sessionKey:
200 OK: the parsed transcript entries (rows that fail JSON parse are dropped):
500 Internal Server Error: a DB failure:
Example
POST /api/chat-history
Batch-inserts transcript entries for a session. Inserts are idempotent; each row carries the entry’s entryId and conflicts on the unique entry_id index do nothing. Entries without an entryId are skipped.
- Request body:
Responses
400 Bad Request: body is not an object:
400 Bad Request: sessionKey missing or entries not a non-empty array:
200 OK: inserted (idempotent on entryId; saved counts the entries received, not the rows actually written):
500 Internal Server Error: a DB failure:
Example
DELETE /api/chat-history
Clears every message for a session. Used when an agent is deleted.
- Query params:
sessionKey(required). - Request body: none.
Responses
400 Bad Request: missing sessionKey:
200 OK: cleared:
500 Internal Server Error: a DB failure:
Example
Graph layout: /api/graph-layout
Persists Ghost Graph node positions in the graph_layouts table, keyed by the (name, gatewayUrl) unique index. name distinguishes scopes (e.g. atlas-radial, team-<id>, default).
GET /api/graph-layout
Loads saved positions for a layout. Note the query param is url, not gatewayUrl.
- Query params:
| Param | Type | Default | Notes |
|---|---|---|---|
name | string | 'default' | The layout scope key |
url | string | '' | The Gateway URL the layout was saved under |
- Request body: none.
Responses
200 OK: the saved layout, or an empty positions map when nothing is stored:
This route never returns an error status. A miss returns
{ positions: {} }, and a thrown DB error is also caught and returned as { positions: {} } (HTTP 200).Example
POST /api/graph-layout
Upserts positions for a layout (conflict on (name, gatewayUrl) updates layoutData + updatedAt).
- Request body:
Responses
400 Bad Request: body is not an object:
400 Bad Request: missing gatewayUrl:
200 OK: upserted:
500 Internal Server Error: a DB failure:
Example
Personality: /api/personality
Stores per-agent personality slider values in the agents.personality_config column as a JSON wrapper { values, customText }. SQLite is the source of truth for slider values; the merged SOUL.md is written separately by the client.
GET /api/personality
Loads an agent’s stored personality values and optional custom text.
- Query params:
agentId(required). - Request body: none.
Responses
400 Bad Request: missing agentId:
200 OK: the stored values, or nulls when nothing is stored or the blob is corrupt:
500 Internal Server Error: a DB failure:
Example
POST /api/personality
Upserts an agent’s personality config. The handler ensures a placeholder agents row exists, then sets personality_config to JSON.stringify({ values, customText }). A blank/whitespace customText is stored as null.
- Request body:
Responses
400 Bad Request: body is not an object:
400 Bad Request: agentId or values missing:
200 OK: upserted:
500 Internal Server Error: a DB failure:
Example
Skills: /api/skills
Tracks skill installs in the skills table. The per-agent association lives in the row’s metadata.agentIds array, so a single skill row can be shared across agents. POST runs a supply-chain injection scan and blocks a flagged install with a 422.
GET /api/skills
Lists installed skills, newest first. With agentId, filters to rows whose metadata.agentIds includes that agent.
- Query params:
agentId(optional). - Request body: none.
Responses
200 OK: the skill rows:
500 Internal Server Error: a DB failure (note: skills: [] is still present):
Example
POST /api/skills
Installs a skill for an agent. Before recording anything, the handler runs scanForInjection over the install blob (name + source + category + author + the raw body). A finding blocks the install with 422 and writes a blocked-install audit row; a clean install is also audited (the forensic trail). On a clean scan, an existing skill row merges the agentId into metadata.agentIds; otherwise a new row is inserted.
- Request body:
Responses
400 Bad Request: body is not an object:
400 Bad Request: a required field is missing:
422 Unprocessable Entity: the injection scan found a destructive / exfil / injection / supply-chain pattern; the install is blocked and audited:
200 OK: installed (merged into an existing row, or a new row inserted):
500 Internal Server Error: a DB failure:
Example
DELETE /api/skills
Removes an agent from a skill’s metadata.agentIds. If that was the last agent, the skill row is deleted entirely; otherwise the row is kept with the agent removed.
- Query params:
id(skill id, required) andagentId(required). - Request body: none.
Responses
400 Bad Request: id or agentId missing:
200 OK: skill not found (idempotent no-op):
200 OK: the agent was the last holder; the row was deleted:
200 OK: the agent was removed but the row remains (other agents still hold it):
500 Internal Server Error: a DB failure:
Example
Exec settings: /api/exec-settings
Stores per-agent execution permission settings in the agents.exec_config column as JSON. Read per agent, read all agents at once during fleet hydration, or upsert one agent.
GET /api/exec-settings
Loads one agent’s execution settings.
- Query params:
agentId(required). - Request body: none.
Responses
400 Bad Request: missing agentId:
200 OK: the parsed exec_config, or null when none is stored:
500 Internal Server Error: a DB failure:
Example
GET /api/exec-settings/all
Returns a map of every agent’s execAsk value. Rows without an exec_config, or with malformed JSON, or whose execAsk is not a string, are skipped. Used during fleet hydration.
- Path/query params: none.
- Request body: none.
Responses
200 OK: the per-agent execAsk map:
500 Internal Server Error: a DB failure:
Example
POST /api/exec-settings
Upserts one agent’s execution settings. The handler ensures a placeholder agents row exists, then stores JSON.stringify(values) in exec_config.
- Request body:
Responses
400 Bad Request: body is not an object:
400 Bad Request: agentId or values missing:
200 OK: upserted:
500 Internal Server Error: a DB failure:
Example
Fleet summary: /api/fleet/summary
A read-only aggregation that joins existing tables/streams into one overview; it never recomputes or re-derives state. It counts live (non-archived) agents per runtime, gets each runtime’s class + health from the adapters and the OpenClaw source, rolls up the last 24h of board tasks and verification verdicts, and counts governance budgets. The per-runtime tile loop is runtime-id-agnostic (open-set runtime strings).
- Path/query params: none.
- Request body: none.
Responses
200 OK: the overview:
A runtime with no agent rows still appears (with zero counts) if an adapter or the OpenClaw source reports for it; OpenClaw is always
connected-substrate and its healthOk reflects whether the server-side source connection is connected.500 Internal Server Error: a failure building the summary:
Example
Boo Zero context: /api/boo-zero/*
Boo Zero is the universal team leader. These routes store the markdown briefs it reads (per-team and global) and a Clawboo-side display-name override. Per-team briefs live in the boo_zero_team_briefs table (FK-cascades on team delete); the global brief and the display name live in the settings key/value table.
A missing brief returns
null content, not a 404; the UI then falls back to a client-side default brief. Likewise a missing display name returns name: null so the caller falls back to the Gateway-side agent name.GET /api/boo-zero/team-briefs/:teamId
Loads a team’s Boo Zero brief.
- Path params:
teamId(required). - Request body: none.
Responses
400 Bad Request: missing teamId:
200 OK: the stored brief, or nulls when none exists:
500 Internal Server Error: a DB failure:
Example
PUT /api/boo-zero/team-briefs/:teamId
Upserts a team’s brief (conflict on teamId updates content + updatedAt).
- Path params:
teamId(required). - Request body:
Responses
400 Bad Request: missing teamId:
400 Bad Request: body missing a string content:
200 OK: upserted:
500 Internal Server Error: a DB failure:
Example
DELETE /api/boo-zero/team-briefs/:teamId
Removes a team’s brief. Idempotent; deleting a non-existent brief is a no-op. (The FK cascade already cleans briefs up when the team itself is deleted; this route is for an explicit user action.)
- Path params:
teamId(required). - Request body: none.
Responses
400 Bad Request: missing teamId:
200 OK: removed (or already absent):
500 Internal Server Error: a DB failure:
Example
GET /api/boo-zero/global-brief
Loads the global Boo Zero brief from the settings key boo-zero:global-brief.
- Path/query params: none.
- Request body: none.
Responses
200 OK: the stored brief, or nulls when unset:
updatedAt is always null on this route; the global brief is stored in the settings KV table, and the handler does not re-query the row’s timestamp.500 Internal Server Error: a DB failure:
Example
PUT /api/boo-zero/global-brief
Sets the global Boo Zero brief.
- Request body:
Responses
400 Bad Request: body missing a string content:
200 OK: saved (updatedAt is Date.now()):
500 Internal Server Error: a DB failure:
Example
GET /api/boo-zero/display-name/:agentId
Loads the Clawboo-side display-name override for Boo Zero, keyed by agent id, from the settings key boo-zero:display-name:<agentId>.
- Path params:
agentId(required). - Request body: none.
Responses
400 Bad Request: missing agentId:
200 OK: the override, or null when unset:
500 Internal Server Error: a DB failure:
Example
PUT /api/boo-zero/display-name/:agentId
Sets the display-name override. The value is trimmed and truncated to 80 chars; an empty string clears the override.
- Path params:
agentId(required). - Request body:
Responses
400 Bad Request: missing agentId:
400 Bad Request: body missing a string name:
200 OK: saved (returns the trimmed/truncated value actually stored):
500 Internal Server Error: a DB failure:
Example
Error envelope
Every error response in this group is the standard envelope{ error: string }, except the skills routes (and the graph-layout POST), which use { ok: false, error: string }. The skills GET 500 additionally carries skills: [], and the skills POST 422 carries findings: InjectionFinding[]. The graph-layout GET never returns an error status; a miss or a thrown error both yield { positions: {} } (HTTP 200).
See also
- Cost dashboard + budgets, the UI over cost records
- Governance API, budgets and the budget kill-switch
- Agents API, the agent registry that
agentIdreferences - Teams API, teams, rules, and team-chat (
teamIdreferences) - Boo Zero, briefs, rules, and display name in the UI
- Using the Ghost Graph, what
graph-layoutpositions - Database schema,
cost_records,chat_messages,graph_layouts,skills,boo_zero_team_briefs,agents - REST API overview