REST API
Unless otherwise specified, all endpoints require the tenant API key in the request header:
Authorization: Bearer <租户API Key>Default address is http://localhost:8787, demo tenant is demo (the apiKey is randomly generated on first startup and printed in the logs). This page is for integration development reference only; ordinary users do not need to care about it.
Starting the Server
# 无模型 Key 也能启动(认证/会话/接口都能测,只是对话会报错)
pnpm run server
# 带真实模型(推荐)
OPENAI_API_KEY=sk-... OPENAI_MODEL=gpt-4o-mini pnpm run serverMain environment variables: PORT (default 8787), DATA_DIR (default ./.zqagent_data), APPROVAL_TIMEOUT_SECONDS (timeout for pending approvals/questions, default 900), TENANT_ID / TENANT_API_KEY / MODEL (demo tenant).
Authentication
export BASE=http://localhost:8787
export TOKEN=zk_xxxx # 取启动日志「已创建演示租户」那一行的 apiKey
# 健康检查(公开)
curl -s $BASE/healthz # => {"status":"ok"}
# 无 Key → 401
curl -s -o /dev/null -w "%{http_code}\n" $BASE/api/v1/sessions # => 401
# 正确 Key → 200
curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $TOKEN" $BASE/api/v1/sessionsCore Endpoints Quick Reference
Sessions
| Method | Path | Description |
|---|---|---|
| GET | /healthz | Health check (public) |
| POST | /api/v1/sessions | Create session |
| GET | /api/v1/sessions | List sessions |
| GET | /api/v1/sessions/{id} | Get session |
| PATCH | /api/v1/sessions/{id} | Rename session |
| DELETE | /api/v1/sessions/{id} | Delete session |
| GET | /api/v1/sessions/{id}/state | Session state (including pending approvals/questions) |
| PATCH | /api/v1/sessions/{id}/model | Switch session model |
| PATCH | /api/v1/sessions/{id}/mode | Switch permission mode (plan / interactive / auto / discuss) |
| PATCH | /api/v1/sessions/{id}/draft | Save input box draft (pass an empty string to clear) |
| POST | /api/v1/sessions/{id}/turns | Submit task |
| POST | /api/v1/sessions/{id}/steer | Steering (supplemental input) while running |
| POST | /api/v1/sessions/{id}/interrupt | Interrupt the current task |
| POST | /api/v1/sessions/{id}/resume | Resume session |
| GET | /api/v1/sessions/{id}/events | Event stream (polling or SSE, supports ?since= for incremental) |
| GET | /api/v1/sessions/{id}/approvals | List pending approvals |
| POST | /api/v1/sessions/{id}/approvals/{aid} | Answer approval |
| GET | /api/v1/sessions/{id}/questions | List pending questions |
| POST | /api/v1/sessions/{id}/questions/{qid} | Answer question |
Projects and Nodes
| Method | Path | Description |
|---|---|---|
| GET / POST | /api/v1/projects | List / create projects (specify the working directory on creation) |
| DELETE | /api/v1/projects/{id} | Delete project |
| GET | /api/v1/models | List available models |
| GET / PUT | /api/v1/config | View / update node config |
| GET / PUT | /api/v1/node/llm | View / set node-level default model |
| GET | /api/v1/node/host | Node host info |
| GET | /api/v1/tools | List tools |
| GET | /api/v1/mcp | List MCP servers |
| PUT | /api/v1/mcp | Replace the entire MCP config (written to the project's .agent/mcp.json; 409 when delivered by the platform) |
| POST | /api/v1/mcp/test | Test connection to a single MCP server (not persisted to disk) |
Usage and Cost
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/usage/summary | Usage summary (by time range / model) |
| GET | /api/v1/usage/records | Detailed records |
| GET | /api/v1/usage/export | Export CSV |
| GET / PUT | /api/v1/usage/pricing | View / update pricing table |
| POST | /api/v1/usage/rebuild | Rebuild summary |
| POST | /api/v1/usage/verify | Verify ledger |
Remote Access and Pairing
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/fleet/status | Node enrollment status |
| POST | /api/v1/fleet/enroll | Enroll with an enrollment code (called by zqagent link) |
| POST | /api/v1/fleet/unlink | Unlink |
| GET | /api/v1/fleet/pairing | Get cloud pairing info (gateway address + namespace/key + nodeId) |
| GET / POST | /api/v1/notifications | View / add notification channels |
| DELETE | /api/v1/notifications/{id} | Delete notification channel |
Quick Start
# 创建会话
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini"}' $BASE/api/v1/sessions
# => {"data":{"sessionId":"e4df66d6...","status":"idle",...}}
export SID=e4df66d6... # 替换为返回的 sessionId
# 提交任务(立即返回,后台异步执行)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"input":"你好,请介绍一下自己"}' $BASE/api/v1/sessions/$SID/turns
# => {"data":{"accepted":true,"status":"running"}}
# 轮询事件(since 游标增量拉取)
curl -s "$BASE/api/v1/sessions/$SID/events?since=0"
# 实时订阅(SSE)
curl -N -H "Authorization: Bearer $TOKEN" -H "Accept: text/event-stream" \
$BASE/api/v1/sessions/$SID/eventsIf the session is currently executing a task,
POST /turnswill automatically be converted into steering injected into the current task (returning{"data":{"accepted":true,"status":"steering"}}) instead of returning an error.
Approvals and Questions
When the model requests an operation with side effects (writing files, executing commands, etc.), it suspends and waits for approval; when calling tools such as ask_user, it suspends and waits for an answer.
# 查看待审批列表(id 就是工具调用 ID)
curl -s -H "Authorization: Bearer $TOKEN" $BASE/api/v1/sessions/$SID/approvals
# 回答审批:once | always_tool | always_command | deny
# always_command 仅对 run_shell 有效
export AID=call_xxx
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"decision":"once"}' $BASE/api/v1/sessions/$SID/approvals/$AID
# 回答问题
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"answer":"继续"}' $BASE/api/v1/sessions/$SID/questions/qid_xxxMulti-Tenant Isolation
Each tenant uses its own API key to access its own sessions; using tenant A's key to access tenant B's session returns 404.
Error Code Quick Reference
| HTTP | code | Scenario |
|---|---|---|
| 401 | unauthorized | No key / invalid key |
| 404 | not_found | Session not found / unknown route / cross-tenant access |
| 409 | not_running | Session is not running when explicitly steering |
| 409 | has_pending_interaction | Pending approvals/questions exist; cannot submit a new task |
| 400 | bad_request | Invalid JSON body / empty input |
| 400 | invalid_decision | Invalid decision, or always_command submitted for a non-run_shell |
