Agent API
Each agent VM runs a FastAPI server with the endpoints documented below. All endpoints except /health require authentication via the Authorization: Bearer <agent_secret> header.
In normal operation, these endpoints are called by the LiberClaw API proxy — not by end users directly. This reference is for developers building integrations or debugging agent behavior.
Authentication
Section titled “Authentication”Every request (except GET /health) must include:
Authorization: Bearer <agent_secret>The agent stores a SHA-256 hash of the secret and validates using constant-time comparison. Requests without a valid token receive a 401 Unauthorized response.
Endpoints
Section titled “Endpoints”GET /health
Section titled “GET /health”Health check endpoint. No authentication required.
Response:
{ "status": "ok", "agent_name": "MyAgent", "version": "1.0.0"}curl https://agent.example.com/healthPOST /chat
Section titled “POST /chat”Send a message and receive an SSE stream of events as the agent processes it. If there is an existing active run for the same chat_id, it is automatically cancelled before starting the new one.
Request body:
{ "message": "Hello, what can you do?", "chat_id": "conv_abc123"}Response: text/event-stream (Server-Sent Events)
Each event is a JSON object on a data: line:
| Event type | Fields | Description |
|---|---|---|
text | content | Text output from the model |
tool_use | name, input | Tool call being executed |
file | path, caption | File being sent to the user |
error | content | Error message |
keepalive | (none) | Connection keepalive (sent every 15s during long operations) |
done | (none) | Stream is complete |
curl -N -X POST https://agent.example.com/chat \ -H "Authorization: Bearer $SECRET" \ -H "Content-Type: application/json" \ -d '{"message": "List files in the workspace", "chat_id": "test"}'Example SSE output:
data: {"type": "tool_use", "name": "list_dir", "input": "{\"path\": \".\"}"}
data: {"type": "text", "content": "Here are the files in your workspace:\n..."}
data: {"type": "done"}GET /chat/{chat_id}/active
Section titled “GET /chat/{chat_id}/active”Check if there is an active (in-progress) chat run for a given chat ID.
Response:
{ "active": true, "user_message": "List files in the workspace"}If no active run exists:
{ "active": false, "user_message": ""}curl https://agent.example.com/chat/test/active \ -H "Authorization: Bearer $SECRET"GET /chat/{chat_id}/stream
Section titled “GET /chat/{chat_id}/stream”Reconnect to an active chat run’s SSE stream. Replays all buffered events from the beginning, then continues streaming live events. The first event is a stream_meta with the original user_message.
If no active run exists, returns a single done event.
curl -N https://agent.example.com/chat/test/stream \ -H "Authorization: Bearer $SECRET"Example output on reconnect:
data: {"type": "stream_meta", "user_message": "List files in the workspace"}
data: {"type": "tool_use", "name": "list_dir", "input": "{\"path\": \".\"}"}
data: {"type": "text", "content": "Here are the files..."}
data: {"type": "done"}GET /chat/{chat_id}/history
Section titled “GET /chat/{chat_id}/history”Return conversation history as a list of chat events.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Maximum number of messages |
Response:
{ "messages": [ {"type": "text", "content": "Hello", "name": "user"}, {"type": "text", "content": "Hi! How can I help?"}, {"type": "tool_use", "name": "bash", "input": "{\"command\": \"ls\"}"}, {"type": "file", "path": "images/chart.png"} ]}User messages include "name": "user". Assistant messages omit the name field.
curl https://agent.example.com/chat/test/history?limit=20 \ -H "Authorization: Bearer $SECRET"DELETE /chat/{chat_id}
Section titled “DELETE /chat/{chat_id}”Clear conversation history for a chat.
Response:
{ "status": "ok", "deleted": 42}curl -X DELETE https://agent.example.com/chat/test \ -H "Authorization: Bearer $SECRET"GET /pending
Section titled “GET /pending”Return pending proactive messages (from heartbeat, subagents) and clear them.
Response:
{ "messages": [ { "chat_id": "owner_123", "content": "[Heartbeat] Updated the daily report.", "source": "heartbeat", "created_at": "2026-02-23T10:30:00" } ]}curl https://agent.example.com/pending \ -H "Authorization: Bearer $SECRET"GET /files/{path}
Section titled “GET /files/{path}”Serve a file from the workspace. The path is relative to the workspace root. Protected by workspace boundary checks and the sensitive file blocklist.
curl https://agent.example.com/files/images/chart.png \ -H "Authorization: Bearer $SECRET" \ --output chart.pngReturns 403 Forbidden for paths that escape the workspace or match sensitive filenames (.env, agent.db).
POST /files/upload
Section titled “POST /files/upload”Upload a file to the agent workspace.
Request: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | file | yes | The file to upload |
path | string | no | Target directory (default: "uploads") |
Response:
{ "path": "uploads/data.csv", "size": 1024, "name": "data.csv"}Maximum file size: 50 MB.
curl -X POST https://agent.example.com/files/upload \ -H "Authorization: Bearer $SECRET" \ -F "file=@data.csv" \ -F "path=uploads"GET /workspace/tree
Section titled “GET /workspace/tree”Return a recursive file tree of the workspace.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
max_depth | integer | 5 | Maximum directory depth |
Response:
{ "tree": [ { "name": "memory", "path": "memory", "type": "dir", "children": [ {"name": "MEMORY.md", "path": "memory/MEMORY.md", "type": "file", "size": 256} ] }, {"name": "HEARTBEAT.md", "path": "HEARTBEAT.md", "type": "file", "size": 0} ]}Directories are listed before files. Sensitive files (.env, agent.db, .git, __pycache__, node_modules) are excluded.
curl https://agent.example.com/workspace/tree?max_depth=3 \ -H "Authorization: Bearer $SECRET"GET /subagents
Section titled “GET /subagents”List all subagent runs. Running subagents are listed first, then sorted by start time (newest first). Completed runs are retained for 1 hour.
Response:
{ "subagents": [ { "id": "a1b2c3d4", "label": "Research task", "task": "Find the latest pricing for...", "status": "running", "chat_id": "conv_abc123", "started_at": 1708700000.0, "completed_at": null, "result_preview": null, "error": null, "duration": 12.5 } ]}Subagent statuses: running, completed, failed, timeout.
curl https://agent.example.com/subagents \ -H "Authorization: Bearer $SECRET"GET /subagents/{run_id}
Section titled “GET /subagents/{run_id}”Get full details of a single subagent run, including the complete task, result, and persona.
Response:
{ "id": "a1b2c3d4", "label": "Research task", "task": "Find the latest pricing for cloud VMs across providers", "persona": "You are a cloud infrastructure researcher", "status": "completed", "chat_id": "conv_abc123", "started_at": 1708700000.0, "completed_at": 1708700045.0, "result": "Based on my research, here are the current prices...", "error": null, "duration": 45.0}curl https://agent.example.com/subagents/a1b2c3d4 \ -H "Authorization: Bearer $SECRET"POST /subagents/{run_id}/stop
Section titled “POST /subagents/{run_id}/stop”Cancel a running subagent. Returns an error if the subagent is not in the running state.
Response:
{ "status": "ok", "run_id": "a1b2c3d4", "message": "Subagent 'Research task' cancelled"}curl -X POST https://agent.example.com/subagents/a1b2c3d4/stop \ -H "Authorization: Bearer $SECRET"GET /telegram/status
Section titled “GET /telegram/status”Return Telegram bot connection status for agents with Telegram integration enabled.
Response (connected):
{ "connected": true, "bot_username": "my_agent_bot", "bot_name": "My Agent"}Response (not connected):
{ "connected": false, "bot_username": "", "bot_name": ""}curl https://agent.example.com/telegram/status \ -H "Authorization: Bearer $SECRET"GET /telegram/contacts
Section titled “GET /telegram/contacts”List Telegram contacts, optionally filtered by status.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | no | Filter by status: allowed, pending, or blocked |
Response:
{ "contacts": [ { "telegram_id": "123456789", "username": "johndoe", "display_name": "John Doe", "status": "allowed" } ]}curl "https://agent.example.com/telegram/contacts?status=pending" \ -H "Authorization: Bearer $SECRET"PATCH /telegram/contacts/{telegram_id}
Section titled “PATCH /telegram/contacts/{telegram_id}”Update a Telegram contact’s status.
Request body:
{ "status": "allowed"}Valid statuses: allowed, pending, blocked.
curl -X PATCH https://agent.example.com/telegram/contacts/123456789 \ -H "Authorization: Bearer $SECRET" \ -H "Content-Type: application/json" \ -d '{"status": "allowed"}'DELETE /telegram/contacts/{telegram_id}
Section titled “DELETE /telegram/contacts/{telegram_id}”Remove a Telegram contact.
Response:
{ "status": "ok", "telegram_id": "123456789"}curl -X DELETE https://agent.example.com/telegram/contacts/123456789 \ -H "Authorization: Bearer $SECRET"