Skip to main content
The Agents API is Clawboo’s registry of record for all Boos (agents). SQLite is the source of truth for who exists; an AgentSource syncs each upstream (the OpenClaw Gateway, the in-process native runtime) into SQLite on startup and on demand. Reads always serve SQLite, so the agent list keeps answering even when the Gateway connection is down — a stale flag marks that case. Writes (create, file updates, sessions) delegate to the owning source and return 503 when that source needs a live upstream connection that is unavailable.
These routes return Clawboo-native AgentRecord shapes, not OpenClaw protocol shapes. A native (clawboo-native) agent is owned by a peer source whose reads and writes are pure SQLite and always work offline.

Routes

MethodPathSummary
GET/api/agentsList all agents from every source
POST/api/agentsCreate an agent through its source
POST/api/agents/syncManual sync of the OpenClaw source
GET/api/agents/registry/healthServer-side OpenClaw connection state
POST/api/agents/cleanup-ghostsSweep stale local rows not in the live set
GET/api/agents/:agentIdOne agent record from its source
DELETE/api/agents/:agentIdArchive upstream and clean local rows
GET/api/agents/:agentId/files/:nameRead one agent file
PUT/api/agents/:agentId/files/:nameWrite one agent file
GET/api/agents/:agentId/sessionsList the agent’s live sessions

The AgentRecord shape

Every response that returns an agent uses this shape:
interface AgentRecord {
  id: string                   // Clawboo primary key (SQLite-native)
  sourceId: string             // 'openclaw' | 'claude-code' | 'codex' | 'hermes' | 'clawboo-native'
  sourceAgentId: string        // upstream id (Gateway-synced)
  displayName: string          // Boo Zero override → identity.name → name → id
  emoji: string | null
  avatarUrl: string | null
  avatarSeed: string | null
  status: 'idle' | 'running' | 'error' | 'sleeping' | 'archived'
  sessionKey: string | null
  isDefault: boolean           // true when this is Boo Zero
  teamId: string | null
  personalityConfig: unknown | null
  execConfig: unknown | null
  participantKind: 'agent' | 'human'
  runtime: string              // 'openclaw' | 'claude-code' | 'codex' | 'hermes' | 'clawboo-native'
  capabilities: unknown | null
  tenantId: string | null      // dormant multi-tenant seam — always null today
  archivedAt: number | null    // soft-delete tombstone (epoch ms); null = live
  createdAt: number
  updatedAt: number
}

GET /api/agents

Returns the merged agent list across every registered source. All data is served from SQLite so the response succeeds even when the Gateway is disconnected.
includeArchived
string
Pass "true" to include soft-deleted (archived) agents. Omit for live agents only.
teamId
string
Filter agents to a specific team by its UUID.

Response 200 OK

defaultId
string
The OpenClaw source’s Boo Zero id. Empty string if unset.
mainKey
string
The OpenClaw main session key. Defaults to "main" if unset.
agents
AgentRecord[]
The merged agent list across all sources.
stale
boolean
true when the server-side OpenClaw connection is not connected. SQLite data is still served.
lastSyncedAt
number | null
Epoch ms of the last successful sync with OpenClaw, or null if never synced.
curl http://localhost:18790/api/agents
curl 'http://localhost:18790/api/agents?teamId=<team-uuid>&includeArchived=true'

POST /api/agents

Creates an agent through its owning source. By default this routes to the OpenClaw source, which returns 503 when the Gateway connection is down. Pass sourceId: "clawboo-native" to create a native agent using only SQLite — no Gateway required.

Request body

name
string
required
The agent’s display name. Trimmed; must be non-empty.
teamId
string
Assign the agent to a team by its UUID.
personalityConfig
object
Personality slider values and optional custom text.
execConfig
object
Execution permission settings (execAsk, execSecurity).
avatarSeed
string
Seed string used to deterministically generate the agent’s avatar.
files
object
Agent files to write at creation time, keyed by filename (e.g. "SOUL.md", "IDENTITY.md").
sourceId
string
The source to create the agent through. Defaults to "openclaw". Pass "clawboo-native" for an offline-capable native agent.

Responses

201 Created — the agent was created:
{ "agent": { "<AgentRecord>" } }
400 Bad Requestname is missing or blank:
{ "error": "name is required" }
503 Service Unavailable — the source needs a live Gateway that is down:
{ "error": "gateway_disconnected" }
# OpenClaw agent — requires a live Gateway
curl -X POST http://localhost:18790/api/agents \
  -H 'Content-Type: application/json' \
  -d '{"name":"Research Boo","teamId":"<team-uuid>"}'

# Native agent — pure SQLite, works offline
curl -X POST http://localhost:18790/api/agents \
  -H 'Content-Type: application/json' \
  -d '{"name":"Native Boo","sourceId":"clawboo-native"}'

GET /api/agents/:agentId

Returns one agent record by its Clawboo id, routed to the source that owns the row.

Response 200 OK

{ "agent": { "<AgentRecord>" } }
404 Not Found — no agent with that id:
{ "error": "agent not found" }
curl http://localhost:18790/api/agents/<agent-id>

DELETE /api/agents/:agentId

Archives the agent. Deletes the upstream record, then cleans the local SQLite row and its FK-referenced children (cost_records, approval_history, per-agent settings keys). If the Gateway is disconnected, falls back to SQLite-only cleanup so the local row never rots.
The browser is responsible for the Gateway agents.delete RPC. This endpoint cleans up Clawboo’s local metadata. Without it, deleted agents leave ghost rows that inflate per-team agent counts.

Responses

200 OK — archived upstream and locally:
{ "ok": true, "upstreamDeleted": true }
200 OK — Gateway was disconnected, so only local rows were cleaned:
{ "ok": true, "upstreamDeleted": false }
curl -X DELETE http://localhost:18790/api/agents/<agent-id>

GET /api/agents/:agentId/files/:name

Reads one agent file through the owning source. The :name segment must be one of the seven canonical filenames:
FilenamePurpose
AGENTS.mdTeam context injected at session start
SOUL.mdPersonality and behavioral guidance
IDENTITY.mdAgent identity and role definition
USER.mdUser self-introduction
TOOLS.mdTool usage preferences
HEARTBEAT.mdAgent routine cadence
MEMORY.mdPersistent memory notes

Responses

200 OK:
{ "name": "SOUL.md", "content": "# Soul\n\n..." }
400 Bad Request — unrecognized filename:
{ "error": "invalid file name" }
503 Service Unavailable — the owning source needs a live Gateway that is down:
{ "error": "gateway_disconnected" }
curl http://localhost:18790/api/agents/<agent-id>/files/SOUL.md

PUT /api/agents/:agentId/files/:name

Writes one agent file through the owning source. Uses the same seven-filename allowlist as the read endpoint.

Request body

content
string
required
The file’s full content. Must be a string.

Response 200 OK

{ "name": "TOOLS.md", "content": "# Tools\n\n- read_file\n" }
curl -X PUT http://localhost:18790/api/agents/<agent-id>/files/TOOLS.md \
  -H 'Content-Type: application/json' \
  -d '{"content":"# Tools\n\n- read_file\n"}'

GET /api/agents/:agentId/sessions

Lists the agent’s live sessions. The OpenClaw source queries the Gateway live, so this returns 503 when disconnected.

Response 200 OK

{
  sessions: Array<{
    id: string
    sourceId: string
    sourceSessionId: string     // the upstream session key
    agentId: string
    teamId: string | null
    status: 'active' | 'idle' | 'closed'
    createdAt: number | null
    updatedAt: number | null
  }>
}
curl http://localhost:18790/api/agents/<agent-id>/sessions

POST /api/agents/sync

Reconciles the OpenClaw source’s upstream into SQLite. With no body it runs a server-side sync (needs the Gateway). With a body containing an agents array it runs a browser-fallback upsert — the browser pushes its own agents.list result so SQLite warms without the server’s own connection.
# Server-side sync
curl -X POST http://localhost:18790/api/agents/sync

# Browser-fallback upsert
curl -X POST http://localhost:18790/api/agents/sync \
  -H 'Content-Type: application/json' \
  -d '{"defaultId":"a1","mainKey":"main","agents":[{"id":"a1","name":"Boo"}]}'

GET /api/agents/registry/health

Reports the server-side OpenClaw connection state. Always returns 200 — this is a liveness surface, not gated by the connection.

Response 200 OK

{
  ok: boolean
  connection: 'connected' | 'connecting' | 'reconnecting' | 'disconnected'
  lastSyncedAt: number | null
  message?: string
}
curl http://localhost:18790/api/agents/registry/health

POST /api/agents/cleanup-ghosts

Sweeps stale local OpenClaw rows not present in the live Gateway set. Pass the ids of all agents currently alive in the Gateway; the endpoint deletes every local OpenClaw-owned row NOT in that list, plus its FK-referenced children.
allowEmpty
string
Pass "true" to permit an empty liveAgentIds array. Guards against a Gateway hiccup nuking all rows.

Request body

liveAgentIds
string[]
required
The ids of all agents currently alive in the Gateway.
curl -X POST http://localhost:18790/api/agents/cleanup-ghosts \
  -H 'Content-Type: application/json' \
  -d '{"liveAgentIds":["a1","a2"]}'

See also