22 KiB
Gateway API
This document describes the HTTP and WebSocket API exposed by clawgo gateway run.
It is based on the routes registered in pkg/api/server.go and the gateway wiring in
cmd/cmd_gateway.go.
Authentication
All /api/* routes and protected extra routes require the gateway token when
gateway.token is non-empty. /health does not require authentication.
Accepted token transports:
Authorization: Bearer <gateway.token>- Query parameter:
?token=<gateway.token> - Cookie:
clawgo_webui_token=<gateway.token> - Browser asset fallback: a request whose
RefererURL contains the sametoken
If gateway.token is empty, API authentication is disabled.
Unauthenticated requests return HTTP 401 with plain text:
unauthorized
Base URL
The gateway listens on gateway.host and gateway.port from config.json.
The sample config uses:
http://localhost:18790
Examples below use:
BASE=http://localhost:18790
TOKEN=<gateway.token>
JSON endpoints use Content-Type: application/json unless noted otherwise.
Live Connections
The current live endpoints are WebSocket endpoints, not SSE endpoints:
GET /api/chat/liveGET /api/events/liveGET /api/logs/live
Connect with the same token rules as HTTP requests, for example:
ws://localhost:18790/api/events/live?token=<gateway.token>
Health Check
GET /health
Purpose: liveness probe for the gateway HTTP process.
Authentication: none.
Response:
ok
Config
GET /api/config
Purpose: read the merged gateway config. Defaults are merged with the configured
config.json content.
Authentication: required when gateway.token is set.
Query parameters:
| Name | Description |
|---|---|
mode=normalized |
Return normalized config view plus raw_config. |
mode=hot |
Return merged config plus hot reload field metadata. |
include_hot_reload_fields=1 |
Include hot reload field names and details. |
Example:
curl -H "Authorization: Bearer $TOKEN" "$BASE/api/config?mode=normalized"
Response:
{
"ok": true,
"config": {},
"raw_config": {}
}
POST /api/config
Purpose: save config and trigger gateway reload behavior.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
mode=raw or omitted |
Body is the raw config shape. |
mode=normalized |
Body is the normalized config view. |
Example body:
{
"gateway": {
"host": "0.0.0.0",
"port": 18790,
"token": ""
}
}
Success response:
{
"ok": true
}
Validation errors return HTTP 400:
{
"ok": false,
"error": "invalid config: ...",
"errors": ["..."]
}
Chat
POST /api/chat
Purpose: send a direct message to the Agent runtime and receive one complete reply.
Authentication: required.
Request body:
| Field | Required | Description |
|---|---|---|
session |
No | Session key. Defaults to query session, then main. |
message |
No | Prompt text. |
media |
No | Uploaded file path. Appended to the prompt as [file: <path>]. |
Example:
curl -X POST "$BASE/api/chat" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"session":"main","message":"Hello"}'
Response:
{
"ok": true,
"reply": "Hello! How can I help?",
"session": "main"
}
Chat History
GET /api/chat/history
Purpose: read stored messages for a session through the gateway history callback.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
session |
Session key. Defaults to main. |
Response:
{
"ok": true,
"session": "main",
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}
Live Chat
GET /api/chat/live
Purpose: WebSocket chat request that streams the completed reply in small JSON chunks.
Authentication: required.
Protocol:
- Client opens the WebSocket.
- Client sends one JSON message with
session,message, and optionalmedia. - Server replies with zero or more
chat_chunkmessages and onechat_donemessage, or achat_errormessage.
Client message:
{
"session": "main",
"message": "Summarize today",
"media": ""
}
Server chunk:
{
"ok": true,
"type": "chat_chunk",
"session": "main",
"delta": "Partial reply text"
}
Done message:
{
"ok": true,
"type": "chat_done",
"session": "main"
}
Error message:
{
"ok": false,
"type": "chat_error",
"error": "invalid json",
"session": "main"
}
Events
GET /api/events/live
Purpose: WebSocket event stream for gateway-side events such as config changes.
Authentication: required.
Initial message:
{
"type": "ready"
}
Known event payload example:
{
"type": "config_changed",
"source": "webui"
}
The connection stays open until the client disconnects.
Version
GET /api/version
Purpose: read gateway build version and compiled channel keys.
Authentication: required.
Response:
{
"ok": true,
"gateway_version": "devel",
"compiled_channels": ["weixin", "telegram", "feishu"]
}
Provider OAuth
Provider endpoints use the configured provider when provider is empty. Provider
updates save config.json and trigger a runtime reload hook when available.
GET|POST /api/provider/oauth/start
Purpose: start a manual OAuth login flow.
Authentication: required.
GET query parameters or POST JSON fields:
| Field | Description |
|---|---|
provider |
Provider name. Defaults to primary provider. |
account_label |
Optional label for the imported account. |
network_proxy |
Optional proxy override. |
provider_config |
POST only. Inline provider config override. |
Response:
{
"ok": true,
"flow_id": "1710000000000000000",
"mode": "manual",
"auth_url": "https://example.com/oauth",
"user_code": "",
"instructions": "Open the URL and paste the callback.",
"account_label": "work",
"network_proxy": ""
}
POST /api/provider/oauth/complete
Purpose: complete a previously started manual OAuth flow and persist credentials.
Authentication: required.
Request body:
{
"provider": "codex",
"flow_id": "1710000000000000000",
"callback_url": "http://localhost:1455/callback?code=...",
"account_label": "work",
"network_proxy": ""
}
Response:
{
"ok": true,
"account": "user@example.com",
"credential_file": "/home/user/.clawgo/auth/codex-work.json",
"network_proxy": "",
"models": ["gpt-5.4"]
}
POST /api/provider/oauth/import
Purpose: import an OAuth credential JSON file with multipart form data.
Authentication: required.
Multipart fields:
| Field | Required | Description |
|---|---|---|
file |
Yes | Auth JSON file. |
provider |
No | Provider name. |
account_label |
No | Account label. |
network_proxy |
No | Proxy override. |
provider_config |
No | JSON encoded inline provider config. |
Response:
{
"ok": true,
"account": "user@example.com",
"credential_file": "/home/user/.clawgo/auth/codex-work.json",
"network_proxy": "",
"models": ["gpt-5.4"]
}
GET /api/provider/oauth/accounts
Purpose: list OAuth accounts for a provider.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
provider |
Provider name. Defaults to primary provider. |
Response:
{
"ok": true,
"accounts": []
}
POST /api/provider/oauth/accounts
Purpose: manage a provider OAuth account.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
provider |
Provider name. Defaults to primary provider. |
Request body:
{
"action": "refresh",
"credential_file": "/home/user/.clawgo/auth/codex-work.json"
}
Supported actions:
refreshdeleteclear_cooldown
Response examples:
{
"ok": true,
"account": {}
}
{
"ok": true,
"deleted": true
}
{
"ok": true,
"cleared": true
}
Provider Models / Runtime
POST /api/provider/models
Purpose: replace the configured model list for a provider.
Authentication: required.
Request body:
{
"provider": "openai",
"model": "gpt-5.4",
"models": ["gpt-5.4", "gpt-5.4-mini"]
}
At least one value from model or models is required.
Response:
{
"ok": true,
"models": ["gpt-5.4", "gpt-5.4-mini"]
}
GET /api/provider/runtime
Purpose: inspect provider runtime state and history.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
provider |
Provider name. Defaults to primary provider where applicable. |
kind |
Runtime event kind filter. |
reason |
Runtime reason filter. |
target |
Runtime target filter. |
sort |
Sort mode passed to provider runtime view. |
changes_only=true |
Include only change events. |
window_sec |
Time window in seconds. |
limit |
Max result count. |
cursor |
Cursor offset. |
health_below |
Filter by health threshold. |
cooldown_until_before_sec |
Filter cooldowns before now plus this many seconds. |
Response:
{
"ok": true,
"view": {}
}
POST /api/provider/runtime
Purpose: operate on provider runtime metadata.
Authentication: required.
Request body:
{
"provider": "codex",
"action": "refresh_now",
"only_expiring": true
}
Supported actions:
clear_api_cooldownclear_historyrefresh_nowrerank
Response examples:
{
"ok": true,
"cleared": true
}
{
"ok": true,
"provider": "codex",
"refreshed": true,
"result": {},
"candidate_order": [],
"summary": {}
}
{
"ok": true,
"provider": "codex",
"reranked": true,
"candidate_order": []
}
Weixin Login
GET /api/weixin/status
Purpose: read Weixin channel status, pending login records, and accounts.
Authentication: required.
Response:
{
"ok": true,
"enabled": false,
"base_url": "https://ilinkai.weixin.qq.com",
"pending_logins": [],
"pending_login": {
"login_id": "",
"qr_available": false
},
"accounts": []
}
If the channel is unavailable, the endpoint returns HTTP 200 with ok: false
and an error field.
POST /api/weixin/login/start
Purpose: start a Weixin QR login flow.
Authentication: required.
Request body: none.
Response: same shape as GET /api/weixin/status.
POST /api/weixin/login/cancel
Purpose: cancel a pending Weixin login by ID.
Authentication: required.
Request body:
{
"login_id": "login-id"
}
Response: same shape as GET /api/weixin/status.
GET /api/weixin/qr.svg
Purpose: render the current or selected pending Weixin login QR code as SVG.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
login_id |
Optional pending login ID. Defaults to the first pending login. |
Response headers:
Content-Type: image/svg+xml
POST /api/weixin/accounts/remove
Purpose: remove a Weixin account by bot ID.
Authentication: required.
Request body:
{
"bot_id": "bot-id"
}
Response: same shape as GET /api/weixin/status.
POST /api/weixin/accounts/default
Purpose: set the default Weixin account by bot ID.
Authentication: required.
Request body:
{
"bot_id": "bot-id"
}
Response: same shape as GET /api/weixin/status.
Upload
POST /api/upload
Purpose: upload a file for later chat usage.
Authentication: required.
Content type: multipart/form-data.
Multipart fields:
| Field | Required | Description |
|---|---|---|
file |
Yes | File to upload. |
The server stores files under the OS temp directory in clawgo_webui_uploads.
Response:
{
"ok": true,
"path": "/tmp/clawgo_webui_uploads/1710000000000000000_input.txt",
"name": "input.txt"
}
Cron
GET /api/cron
Purpose: list cron jobs or read one cron job.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
id |
Optional job ID. When present, returns one job. |
List response:
{
"ok": true,
"jobs": []
}
Single job response:
{
"ok": true,
"job": {}
}
POST /api/cron
Purpose: create or mutate cron jobs through the gateway cron handler.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
id |
Optional job ID copied into the request args. |
Request body:
{
"action": "create",
"name": "daily-check",
"message": "Run daily check",
"expr": "0 9 * * *",
"deliver": false,
"channel": "",
"to": ""
}
Supported actions from the gateway runtime:
createupdatedeleteenabledisablegetlist
Legacy scheduling fields kind, everyMs, and atMs are still accepted by the
runtime for older clients.
Response:
{
"ok": true,
"result": {}
}
Skills
GET /api/skills
Purpose: list skills, inspect a skill's files, or read a skill file.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
check_updates=1 |
Check ClawHub for remote versions when clawhub is installed. |
id |
Skill ID for detail operations. |
files=1 |
With id, list files in that skill. |
file |
With id, read one relative text file. |
List response:
{
"ok": true,
"skills": [],
"source": "clawhub",
"clawhub_installed": false,
"clawhub_path": ""
}
Files response:
{
"ok": true,
"id": "example",
"files": ["SKILL.md"]
}
File response:
{
"ok": true,
"id": "example",
"file": "SKILL.md",
"content": "# example"
}
POST /api/skills
Purpose: import, install, enable, disable, create, update, or write skill files.
Authentication: required.
Multipart upload:
Content-Type: multipart/form-datafile:.zip,.tar,.tar.gz, or.tgzarchive containing one or moreSKILL.mdfiles.
Multipart response:
{
"ok": true,
"imported": ["my-skill"]
}
JSON body fields:
| Field | Description |
|---|---|
action |
install, enable, disable, write_file, create, or update. |
id |
Existing skill ID. |
name |
Skill name. Defaults to id. |
description |
Used by create and update. |
tools |
String list used by create and update. |
system_prompt |
Used by create and update. |
file |
Relative file path for write_file. |
content |
File content for write_file. |
ignore_suspicious |
Adds --force to ClawHub install. |
Example:
{
"action": "disable",
"name": "example"
}
Response:
{
"ok": true
}
DELETE /api/skills
Purpose: delete an enabled or disabled skill directory.
Authentication: required.
Query parameters:
| Name | Required | Description |
|---|---|---|
id |
Yes | Skill ID. |
Response:
{
"ok": true,
"deleted": true,
"id": "example"
}
Sessions
GET /api/sessions
Purpose: list user-facing session keys found in the main agent session store.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
include_internal=1 |
Include internal, subagent, heartbeat, cron, and hook sessions. |
Response:
{
"ok": true,
"sessions": [
{
"key": "main",
"channel": "main"
}
]
}
Memory
GET /api/memory
Purpose: list memory files or read one memory file.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
path |
Optional relative path. MEMORY.md reads from workspace root. |
List response:
{
"ok": true,
"files": ["MEMORY.md"]
}
File response:
{
"ok": true,
"path": "MEMORY.md",
"content": "..."
}
POST /api/memory
Purpose: write a memory file under workspace/memory.
Authentication: required.
Request body:
{
"path": "notes.md",
"content": "Remember this."
}
Response:
{
"ok": true,
"path": "notes.md"
}
DELETE /api/memory
Purpose: delete a memory file under workspace/memory.
Authentication: required.
Query parameters:
| Name | Required | Description |
|---|---|---|
path |
Yes | Relative memory file path. |
Response:
{
"ok": true,
"deleted": true,
"path": "notes.md"
}
Workspace File
GET /api/workspace_file
Purpose: read a relative text file under the configured workspace.
Authentication: required.
Query parameters:
| Name | Required | Description |
|---|---|---|
path |
Yes | Relative workspace path. Absolute and .. paths are rejected. |
Response:
{
"ok": true,
"path": "AGENTS.md",
"found": true,
"content": "..."
}
POST /api/workspace_file
Purpose: write a relative text file under the configured workspace. Parent directories are created when needed.
Authentication: required.
Request body:
{
"path": "notes/today.md",
"content": "..."
}
Response:
{
"ok": true,
"path": "notes/today.md",
"saved": true
}
Tools
GET /api/tool_allowlist_groups
Purpose: read built-in tool allowlist group definitions.
Authentication: required.
Response:
{
"ok": true,
"groups": []
}
GET /api/tools
Purpose: read the runtime tool catalog plus MCP-specific tool and server checks.
Authentication: required.
Response:
{
"tools": [],
"mcp_tools": [],
"mcp_server_checks": [
{
"name": "context7",
"enabled": false,
"transport": "stdio",
"status": "disabled",
"message": "server is disabled",
"command": "npx",
"resolved": "",
"package": "@upstash/context7-mcp",
"installer": "",
"installable": false,
"missing_command": false
}
]
}
Note: this endpoint currently does not include an ok field.
MCP Install
POST /api/mcp/install
Purpose: install an MCP package through a supported package manager and resolve the installed binary path.
Authentication: required.
Request body:
{
"package": "example-mcp",
"installer": "uv"
}
Supported installers:
uv(default): runsuv tool install <package>bun: runsbun add -g <package>
Response:
{
"ok": true,
"package": "example-mcp",
"output": "installed example-mcp via uv",
"bin_name": "example-mcp",
"bin_path": "/home/user/.local/bin/example-mcp"
}
Logs
GET /api/logs/recent
Purpose: read recent log entries from the configured gateway log file.
Authentication: required.
Query parameters:
| Name | Description |
|---|---|
limit |
Number of lines to scan. Defaults to 10, maximum 200. |
Response:
{
"ok": true,
"logs": [
{
"time": "2026-05-10T00:00:00Z",
"level": "INFO",
"msg": "gateway started"
}
]
}
JSON log lines are returned as parsed objects. Plain text lines are wrapped as
time, level, and msg.
GET /api/logs/live
Purpose: WebSocket tail of new log entries from the configured gateway log file.
Authentication: required.
Server message:
{
"ok": true,
"type": "log_entry",
"entry": {
"time": "2026-05-10T00:00:00Z",
"level": "INFO",
"msg": "gateway started"
}
}
If the log file cannot be opened after the WebSocket upgrade, the server sends:
{
"ok": false,
"error": "open ...: no such file or directory"
}
Protected Extra Routes
GET /v1/ws
Purpose: AIStudio relay WebSocket route registered by the gateway with
SetProtectedRoute.
Authentication: required.
Provider selection:
- Query parameter:
provider - Header:
X-Clawgo-Provider - Default:
aistudio
This route is owned by pkg/wsrelay and is documented here only because the
gateway registers it as a protected API-facing route.
Error Responses
Handlers mostly use Go's http.Error, so many errors are plain text with an
HTTP status code instead of JSON.
Common responses:
| Status | Body | Meaning |
|---|---|---|
400 |
invalid json or validation text |
Invalid request body, missing required field, invalid path, or unsupported action. |
401 |
unauthorized |
Missing or invalid gateway token. |
404 |
... not found |
Requested skill, login, QR, or resource was not found. |
405 |
method not allowed |
HTTP method is not supported by that route. |
412 |
clawhub is not installed... |
Skill install requires ClawHub. |
429 |
clawhub rate limit exceeded... |
ClawHub rejected skill lookup or install due to rate limiting. |
500 |
error text | Gateway config, filesystem, log, cron, or runtime handler failure. |
502 |
error text | Upstream channel/login operation failed. |
503 |
weixin channel unavailable |
Weixin route needs an active Weixin channel. |
JSON validation responses may look like:
{
"ok": false,
"error": "invalid config: ...",
"errors": ["..."]
}
WebSocket endpoints report post-upgrade errors as JSON messages when possible.
Common Flows
Send a chat message
curl -X POST "$BASE/api/chat" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"session":"main","message":"Ping"}'
Upload a file and reference it in chat
UPLOAD_PATH=$(
curl -s -X POST "$BASE/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@./notes.txt" |
jq -r .path
)
curl -X POST "$BASE/api/chat" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"session\":\"main\",\"message\":\"Read this file\",\"media\":\"$UPLOAD_PATH\"}"
Start and complete OAuth
curl -X POST "$BASE/api/provider/oauth/start" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"provider":"codex","account_label":"work"}'
Open the returned auth_url, then call:
curl -X POST "$BASE/api/provider/oauth/complete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"provider":"codex","flow_id":"<flow_id>","callback_url":"<callback_url>"}'
Watch config and log events
Use WebSocket clients against:
ws://localhost:18790/api/events/live?token=<gateway.token>
ws://localhost:18790/api/logs/live?token=<gateway.token>