Skip to content

AI Assistant

Chat with Claude/GPT/local models, conversation history, web console deploy of OpenClaw agents, API keys, system prompts.

All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.

Endpoint count: 42

Endpoints

Method Path Handler Guards
POST /api/ai/advanced/clear-sessions aiAdvClearSessions AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/advanced/purge-credentials aiAdvPurgeCredentials AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/advanced/reset-config aiAdvResetConfig AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/advanced/restart-gateway aiAdvRestartGateway AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/chat aiChat AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/chat-stream aiChatStreamV2 AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/chat/poll aiStatus AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/config getOpenClawConfig AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/config saveOpenClawConfig AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/crontab getAiCrontab RequirePermission(db, "administrative_operations")
PUT /api/ai/crontab setAiCrontab RequirePermission(db, "administrative_operations")
POST /api/ai/destroy destroyAiUser RequirePermission(db, "administrative_operations")
POST /api/ai/pairing/approve approveAiPairing RequirePermission(db, "administrative_operations")
POST /api/ai/reconfigure-hive reconfigureHive RequirePermission(db, "administrative_operations")
POST /api/ai/restart restartAiService RequirePermission(db, "administrative_operations")
POST /api/ai/setup setupAiAgent RequirePermission(db, "administrative_operations")
GET /api/ai/status getAiStatus RequirePermission(db, "administrative_operations")
POST /api/ai/stop stopAiService RequirePermission(db, "administrative_operations")
GET /api/ai/telegram/config getTelegramConfig AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/telegram/config saveTelegramConfig AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/telegram/pairings getTelegramPairings AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/telegram/pairings/approve approveTelegramPairing AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/tui aiTUI -
POST /api/ai/update-openclaw updateOpenClaw AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/update-openclaw/status getOpenClawUpdateStatus AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/vault listVaultSecrets RequirePermission(db, "administrative_operations")
POST /api/ai/vault createVaultSecret RequirePermission(db, "administrative_operations")
DELETE /api/ai/vault/:name deleteVaultSecret RequirePermission(db, "administrative_operations")
POST /api/ai/vault/export exportVault RequirePermission(db, "administrative_operations")
POST /api/ai/vault/import importVault RequirePermission(db, "administrative_operations")
POST /api/ai/webconsole/disable-lan webConsoleDisableLAN AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/webconsole/enable-lan webConsoleEnableLAN AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/webconsole/fix webConsoleFix AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/webconsole/healthcheck webConsoleHealthcheck AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/webconsole/restart webConsoleRestart AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/webconsole/start webConsoleStart AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/webconsole/status webConsoleStatus AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/webconsole/stop webConsoleStop AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/whatsapp/config getWhatsAppConfig AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/whatsapp/config saveWhatsAppConfig AuthWithDB(cfg.JWTSecret, db)
GET /api/ai/whatsapp/pairings getWhatsAppPairings AuthWithDB(cfg.JWTSecret, db)
POST /api/ai/whatsapp/pairings/approve approveWhatsAppPairing AuthWithDB(cfg.JWTSecret, db)

Source

Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:

{ "data": { ... } }

On error:

{ "error": { "code": "string", "message": "string" } }

Reference

POST /api/ai/advanced/clear-sessions

Clears all OpenClaw agent chat sessions by running openclaw sessions clear as the ai user.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "output": "command output text" } }

Side effects

  • Runs su - ai -c 'openclaw sessions clear', deleting the agent's stored conversation sessions.

Example

curl -X POST https://panel.example.com:2087/api/ai/advanced/clear-sessions \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/advanced/purge-credentials

Removes all stored AI credentials: clears the credentials directory, empties the vault, deletes .env files, strips API keys/tokens from openclaw.json, and restarts the gateway.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "output": "✅ Cleared credentials/\n✅ Stripped API keys from openclaw.json\n🔄 Gateway restarted\n⚠️  All credentials removed. Reconfigure providers in Config tab." } }

Side effects

  • rm -rf /home/ai/.openclaw/credentials/*.
  • Overwrites /home/ai/.openclaw/vault/secrets.json with {"secrets":[]}.
  • Removes .env / *.env files under /home/ai/.openclaw.
  • Strips apiKey/token/key from auth profiles and channel tokens in openclaw.json via an inline python3 script.
  • Restarts the OpenClaw gateway (su - ai -c 'openclaw gateway restart').

Example

curl -X POST https://panel.example.com:2087/api/ai/advanced/purge-credentials \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/advanced/reset-config

Resets openclaw.json to defaults (preserving the auth section), backing up the existing config first, then restarts the gateway.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "output": "✅ Configuration reset to defaults (credentials preserved)\n🔄 Gateway restarted" } }

Side effects

  • Backs up /home/ai/.openclaw/openclaw.json to openclaw.json.bak.<epoch>.
  • Rewrites openclaw.json keeping only auth, plus default agents and empty channels (default model anthropic/claude-sonnet-4-6, workspace /home/ai/.openclaw/workspace).
  • chown ai:ai the config and restarts the gateway.

Example

curl -X POST https://panel.example.com:2087/api/ai/advanced/reset-config \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/advanced/restart-gateway

Restarts the OpenClaw gateway and returns its status output.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "output": "gateway restarted ... status output" } }

Side effects

  • Runs su - ai -c 'openclaw gateway restart' followed by openclaw gateway status.

Example

curl -X POST https://panel.example.com:2087/api/ai/advanced/restart-gateway \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/chat

Sends a single message to the OpenClaw agent over its local HTTP API and returns the agent's reply (non-streaming).

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "message": "string (required)", "sessionKey": "string (optional)" }
Field Type Required Description
message string yes The user message to send to the agent.
sessionKey string no Session identifier; defaults to agent:main:main.

Response - 200 OK

{ "data": { "reply": "agent reply text" } }

If no reply is produced, reply is the literal string (no reply).

Errors

Status Code Meaning
400 bad_request Body failed binding (e.g. missing message).
503 ai_not_configured Gateway token not found in openclaw.json; AI agent not set up.
503 gateway_error HTTP request to the local OpenClaw gateway failed.

Example

curl -X POST https://panel.example.com:2087/api/ai/chat \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message": "Restart nginx", "sessionKey": "agent:main:main"}'

POST /api/ai/chat-stream

Streams the OpenClaw agent's response token-by-token over Server-Sent Events by relaying a WebSocket session with the local gateway.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "message": "string (required)", "sessionKey": "string (optional)" }
Field Type Required Description
message string yes The user message to send to the agent.
sessionKey string no Session identifier; defaults to agent:main:main.

Response - 200 OK

Content-Type: text/event-stream. Each SSE data: line carries a JSON object: an initial {"type":"start"}, then {"type":"chunk","content":"..."} deltas, ending with {"type":"done","content":"<full text>"}.

Errors

Status Code Meaning
400 bad_request message is required.
503 ai_not_configured Gateway token not found; AI agent not set up.
503 gateway_unavailable Could not open WebSocket to the gateway.
503 challenge_failed Failed reading the gateway challenge frame.
503 auth_failed Failed reading the gateway hello/auth response.
503 auth_rejected Gateway rejected the connection.

Example

curl -X POST https://panel.example.com:2087/api/ai/chat-stream \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message": "Tail the error log"}'

GET /api/ai/chat/poll

Polls the latest assistant reply from the agent:main:main session history (used for polling-based chat UIs).

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "status": "ok", "lastReply": "latest assistant message", "timestamp": 1718630000 } }

status is not_configured when no gateway token is found, or error (with an error field) when the gateway request fails. This handler always responds 200.

Example

curl https://panel.example.com:2087/api/ai/chat/poll \
  -H "Authorization: Bearer $TOKEN"

GET /api/ai/config

Returns the parsed openclaw.json config plus per-provider API keys read from the credentials directory.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "config": { "gateway": {}, "agents": {} }, "credentials": { "anthropic": "sk-...", "custom_base_url": "https://..." } } }

credentials includes any of anthropic, openai, google, deepseek, groq, custom whose default.key file exists, plus custom_base_url if set.

Errors

Status Code Meaning
500 read_failed Could not read /home/ai/.openclaw/openclaw.json.
500 parse_failed Config file is not valid JSON.

Example

curl https://panel.example.com:2087/api/ai/config \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/config

Saves the OpenClaw config and writes provider credentials to credential files, an .env file, an auth-profiles file, and (for a custom gateway) a systemd environment override.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "config": { "gateway": {} }, "credentials": { "anthropic": "sk-...", "custom_base_url": "https://api.example.com" } }
Field Type Required Description
config object no Full OpenClaw config object written to openclaw.json.
credentials object (string→string) no Map of provider name to API key; custom_base_url sets the custom gateway base URL.

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 invalid_json Request body could not be bound.
500 marshal_failed Config could not be serialized.
500 write_failed Could not write openclaw.json.

Side effects

  • Saves a version-history snapshot of .openclaw/openclaw.json, then overwrites it (chown ai:ai).
  • Writes each provider key to /home/ai/.openclaw/credentials/<provider>/default.key and the custom base URL to credentials/custom/base_url.
  • Writes /home/ai/.openclaw/.env with <PROVIDER>_API_KEY lines (custom → OPENAI_API_KEY/OPENAI_BASE_URL).
  • Writes /home/ai/.openclaw/agents/main/agent/auth-profiles.json.
  • For a custom gateway, writes a systemd override at /etc/systemd/system/<svc>.service.d/gateway.conf and runs systemctl daemon-reload; otherwise removes that override and the custom credential files and reloads the daemon.

Example

curl -X POST https://panel.example.com:2087/api/ai/config \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"config": {"gateway": {"mode": "local"}}, "credentials": {"anthropic": "sk-ant-..."}}'

GET /api/ai/crontab

Returns the ai user's crontab entries parsed into schedule/command pairs.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "data": [ { "schedule": "0 3 * * *", "command": "openclaw run nightly" } ] }

The data array is empty/null when the crontab has no entries. Lines starting with @ (e.g. @daily) are parsed as a single-token schedule.

Example

curl https://panel.example.com:2087/api/ai/crontab \
  -H "Authorization: Bearer $TOKEN"

PUT /api/ai/crontab

Replaces the ai user's crontab with the supplied jobs.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "jobs": [ { "schedule": "0 3 * * *", "command": "openclaw run nightly" } ] }
Field Type Required Description
jobs array of {schedule, command} yes Cron entries to install. Entries with an empty schedule or command are skipped.

Response - 200 OK

{ "status": "ok" }

This handler returns a flat (non-enveloped) body.

Errors

Status Code Meaning
400 bad_request Body failed binding.
500 (none) Failed to update crontab when crontab -u ai - fails.

Side effects

  • Pipes the rendered crontab to crontab -u ai -, overwriting the ai user's crontab.

Example

curl -X PUT https://panel.example.com:2087/api/ai/crontab \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jobs": [{"schedule": "0 3 * * *", "command": "openclaw run nightly"}]}'

POST /api/ai/destroy

Completely removes the AI user: stops/disables its services, kills its processes, deletes its home directory and system account, and purges its panel DB records.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "confirm": "DESTROY" }
Field Type Required Description
confirm string yes Must equal DESTROY exactly, or the request is rejected.

Response - 200 OK

{ "data": { "status": "success", "steps": ["Stopped and disabled all AI services", "Removed /home/ai directory"], "errors": [], "message": "AI user destroyed. Re-enable from the AI tab if needed." } }

status is partial when any step recorded an error (errors are listed in errors).

Errors

Status Code Meaning
400 confirmation_required Body missing or confirm is not DESTROY.

Side effects

  • systemctl stop/disable and removes service files for openclaw, openclaw-ai, ai-agent; runs systemctl daemon-reload.
  • pkill -u ai / killall -u ai.
  • os.RemoveAll("/home/ai") (vault, config, workspace).
  • userdel ai.
  • Deletes the ai row from users and its api_keys rows.
  • Removes ai entries from /etc/csf/csf.pignore via sed.

Example

curl -X POST https://panel.example.com:2087/api/ai/destroy \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": "DESTROY"}'

POST /api/ai/pairing/approve

Approves a pending Telegram device pairing by code via the OpenClaw CLI.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "code": "string (required)" }
Field Type Required Description
code string yes Pairing code; trimmed and upper-cased before use.

Response - 200 OK

{ "data": { "status": "ok", "output": "command output" } }

Errors

Status Code Meaning
400 bad_request Body failed binding (missing code).
500 pairing_failed The openclaw pairing approve telegram command failed (message contains its output).

Side effects

  • Runs su - ai -c 'openclaw pairing approve telegram <code>'.

Example

curl -X POST https://panel.example.com:2087/api/ai/pairing/approve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "ABC123"}'

POST /api/ai/reconfigure-hive

Reconfigures the gateway for "hive" mode: token auth, LAN bind, Telegram disabled, device auth bypassed, and a chosen default model.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "model": "anthropic/claude-sonnet-4-20250514" }
Field Type Required Description
model string yes Primary model id set as agents.defaults.model.primary.

Response - 200 OK

{ "status": "ok", "message": "Hive mode configured", "token": "<48-char hex>", "model": "anthropic/claude-sonnet-4-20250514" }

Flat (non-enveloped) body. A freshly generated gateway token is returned.

Errors

Status Code Meaning
400 bad_request model is required.
500 token_gen_failed Random token generation failed.
500 (none) status: "partial" with errors array when one or more openclaw config set commands fail.

Side effects

  • Runs a series of su - ai -c 'timeout 15 openclaw config set <key> <value>' commands setting gateway.auth.mode=token, gateway.auth.token=<new>, gateway.bind=lan, channels.telegram.enabled=false, gateway.controlUi.dangerouslyDisableDeviceAuth=true, and agents.defaults.model.primary=<model>.

Example

curl -X POST https://panel.example.com:2087/api/ai/reconfigure-hive \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model": "anthropic/claude-sonnet-4-20250514"}'

POST /api/ai/restart

Restarts the active OpenClaw systemd service.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "status": "ok", "message": "AI service restarted (openclaw)" }

Flat (non-enveloped) body. The service name is auto-detected (openclaw-ai or openclaw).

Errors

Status Code Meaning
500 restart_failed systemctl restart <svc> failed.

Side effects

  • Runs systemctl restart <openclaw service>.

Example

curl -X POST https://panel.example.com:2087/api/ai/restart \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/setup

Provisions the AI agent: creates the ai OS/panel user if needed, issues an API key, and runs the CLI setup_ai_agent with the chosen provider and credentials.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "provider": "anthropic", "apiKey": "sk-...", "telegramToken": "123:ABC", "telegramOwnerId": "12345678" }
Field Type Required Description
provider string yes One of anthropic, openai, google.
apiKey string yes Provider API key.
telegramToken string yes Telegram bot token.
telegramOwnerId string no Telegram owner/user id.

Response - 200 OK

{ "status": "ok" }

Flat (non-enveloped) body.

Errors

Status Code Meaning
400 bad_request Body failed binding, or provider not in the allowed set.
500 db_error Failed creating the ai user record or reading its id, or inserting the API key.
500 server_error Failed to generate the API key.
500 setup_failed The CLI setup_ai_agent command failed.

Side effects

  • On first setup: creates OS user ai (random password via useradd_starkone), ensures /home/ai and .ssh exist with correct ownership, inserts an admin/superuser ai row in users, writes /etc/sudoers.d/ai-nopasswd, and adds ai to sudo/wheel.
  • Deletes any existing ai-agent API key, then inserts a new hashed bap_... key for the ai user.
  • Runs CLI setup_ai_agent <provider> <apiKey> <telegramToken> <token> <telegramOwnerId>.

Example

curl -X POST https://panel.example.com:2087/api/ai/setup \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider": "anthropic", "apiKey": "sk-ant-...", "telegramToken": "123:ABC", "telegramOwnerId": "12345678"}'

GET /api/ai/status

Reports whether OpenClaw is installed, configured, and running, plus memory and uptime.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "data": { "installed": true, "configured": true, "running": true, "memory_mb": 128.5, "uptime_minutes": 42 } }

Side effects

  • Read-only inspection: os.Stat of config/binary, pgrep -u ai, ps -u ai, and systemctl show openclaw. (No state change.)

Example

curl https://panel.example.com:2087/api/ai/status \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/stop

Stops the OpenClaw service, optionally force-killing all related processes.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "force": false }
Field Type Required Description
force boolean no When true, force-kill all openclaw processes before stopping the service.

Response - 200 OK

{ "status": "ok", "message": "AI service stopped" }

Flat (non-enveloped) body; message is AI service force-killed when force is true.

Side effects

  • When force: killall -9 openclaw-gateway, killall -9 openclaw, pkill -9 -u ai -f openclaw.
  • Always: systemctl stop <openclaw service>.

Example

curl -X POST https://panel.example.com:2087/api/ai/stop \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"force": true}'

GET /api/ai/telegram/config

Returns the Telegram channel config extracted from openclaw.json.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "botToken": "123:ABC", "dmPolicy": "allowlist", "groupPolicy": "allowlist", "allowFrom": ["12345678"], "requireMention": true, "groups": ["-100123"] } }

Returns { "data": {} } when the config file is missing or unparseable. requireMention is read from the * group entry (default true); groups lists non-* group ids.

Example

curl https://panel.example.com:2087/api/ai/telegram/config \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/telegram/config

Writes the Telegram channel config into openclaw.json.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "botToken": "123:ABC", "dmPolicy": "allowlist", "groupPolicy": "allowlist", "allowFrom": ["12345678"], "groups": ["-100123"], "requireMention": true }
Field Type Required Description
botToken string no Telegram bot token.
dmPolicy string no Direct-message policy.
groupPolicy string no Group-message policy.
allowFrom array of string no Allowed sender ids.
groups array of string no Specific group ids to add (besides the * default).
requireMention boolean no Whether the bot must be mentioned in groups (stored under the * group).

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 (none) Body failed binding (error.message only).
500 (none) Failed writing openclaw.json (error.message only).

Side effects

  • Merges a channels.telegram object into openclaw.json, writes the file, and chown ai:ai.

Example

curl -X POST https://panel.example.com:2087/api/ai/telegram/config \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"botToken": "123:ABC", "requireMention": true, "allowFrom": ["12345678"]}'

GET /api/ai/telegram/pairings

Lists pending/known Telegram pairings via the OpenClaw CLI.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "pairings": [] } }

pairings is the JSON array returned by openclaw pairing list telegram --json (empty array on error).

Example

curl https://panel.example.com:2087/api/ai/telegram/pairings \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/telegram/pairings/approve

Approves a Telegram pairing by code via the OpenClaw CLI.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "code": "ABC123" }
Field Type Required Description
code string no Pairing code (passed directly to the CLI; no binding constraint).

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 (none) Body failed binding (error.message only).
500 (none) Failed to approve pairing when the CLI command fails.

Side effects

  • Runs su - ai -c 'openclaw pairing approve telegram <code>'.

Example

curl -X POST https://panel.example.com:2087/api/ai/telegram/pairings/approve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "ABC123"}'

GET /api/ai/tui

Upgrades the connection to a WebSocket terminal running the OpenClaw TUI as the ai user. Authentication is via a JWT in the token query param (not the bearer header).

Guards: - (no router guard; authenticated inline via the token query param, which must be a valid JWT with superuser: true)

Query parameters

Name Type Optional Description
token string no JWT carrying superuser claims; required to authorize the WebSocket upgrade.

Response - 200 OK

Not a JSON body - the connection is upgraded to a WebSocket streaming an interactive openclaw tui --session main terminal.

Errors

Status Code Meaning
401 unauthorized Missing token query param.
403 forbidden Token invalid or not a superuser.

Side effects

  • Reads the gateway token from openclaw.json, auto-approves any pending OpenClaw devices, and spawns an interactive openclaw tui process for the ai user over the socket.

Example

# WebSocket endpoint; connect with a WS client:
wss://panel.example.com:2087/api/ai/tui?token=$JWT

POST /api/ai/update-openclaw

Streams the OpenClaw npm update process (and gateway restart) over Server-Sent Events.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

Content-Type: text/event-stream. Emits progress lines as data: events (e.g. update output, restart messages, final version), terminating with a [DONE] event.

Errors

Status Code Meaning
500 (none) streaming not supported if the writer does not support flushing.

Side effects

  • Runs npm update -g openclaw, then su - ai -c 'openclaw gateway restart', then openclaw --version.

Example

curl -X POST https://panel.example.com:2087/api/ai/update-openclaw \
  -H "Authorization: Bearer $TOKEN"

GET /api/ai/update-openclaw/status

Reports the installed OpenClaw version, the latest npm version, and whether an update is available.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "installed": "2026.4.15", "latest": "2026.5.1", "update_available": true } }

Side effects

  • Read-only: runs openclaw --version and npm view openclaw version.

Example

curl https://panel.example.com:2087/api/ai/update-openclaw/status \
  -H "Authorization: Bearer $TOKEN"

GET /api/ai/vault

Lists vault secret metadata (names/types, no values) via the vault skill script.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "data": { "secrets": [ { "name": "stripe_key", "type": "api_key", "description": "..." } ] } }

Returns { "data": { "secrets": [] } } if the script output is not valid JSON.

Errors

Status Code Meaning
500 vault_error The vault.sh list command failed.

Side effects

  • Runs su - ai -c 'bash /home/ai/.openclaw/skills/vault/vault.sh list' (read-only).

Example

curl https://panel.example.com:2087/api/ai/vault \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/vault

Creates or updates a vault secret via the vault skill script.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "name": "stripe_key", "type": "api_key", "value": "sk-...", "description": "Stripe live key" }
Field Type Required Description
name string yes Secret name.
type string yes One of api_key, password, ssh_key, token, json_array, secret.
value string yes Secret value.
description string no Human-readable description.

Response - 200 OK

{ "data": { "success": true, "message": "command output" } }

Errors

Status Code Meaning
400 bad_request Body failed binding, or type not in the allowed set.
500 vault_error The vault.sh set command failed (message contains its output).

Side effects

  • Runs su - ai -c 'bash vault.sh set <name> <type> <value> <description>', writing the secret into the vault.

Example

curl -X POST https://panel.example.com:2087/api/ai/vault \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "stripe_key", "type": "api_key", "value": "sk-...", "description": "Stripe live key"}'

DELETE /api/ai/vault/:name

Deletes a vault secret by name via the vault skill script.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
name string Name of the secret to delete.

Response - 200 OK

{ "data": { "success": true, "message": "command output" } }

Errors

Status Code Meaning
400 bad_request Secret name missing.
500 vault_error The vault.sh delete command failed.

Side effects

  • Runs su - ai -c 'bash vault.sh delete <name>'.

Example

curl -X DELETE https://panel.example.com:2087/api/ai/vault/stripe_key \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/vault/export

Exports all vault secrets as an encrypted tar.gz download (always encrypted with an internal key, plus the user password if supplied).

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "password": "optional passphrase" }
Field Type Required Description
password string no Extra passphrase; when present, the archive is encrypted a second time with it.

Response - 200 OK

Streams a file (Content-Type: application/octet-stream, Content-Disposition: attachment; filename=vault-export.tar.gz.enc) - not a JSON envelope.

Errors

Status Code Meaning
400 empty_vault No secrets to export.
500 temp_error Could not create temp dir.
500 vault_error Failed listing secrets.
500 parse_error Failed parsing the secret list.
500 tar_error tar failed.
500 encrypt_error openssl encryption failed.

Side effects

  • Writes secret values/metadata to a temp dir, tars them, and runs openssl enc -aes-256-cbc (once with the internal key, again with the user password if given). Temp dir is removed afterward.

Example

curl -X POST https://panel.example.com:2087/api/ai/vault/export \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"password": "passphrase"}' \
  -o vault-export.tar.gz.enc

POST /api/ai/vault/import

Imports secrets from an uploaded encrypted vault archive (multipart form), decrypting and restoring each secret.

Guards: RequirePermission(db, "administrative_operations")

Request body

Multipart form, not JSON:

Field Type Required Description
file file yes The exported .tar.gz.enc archive.
password form field no User passphrase used during export (if any).
overwrite form field no 1 to overwrite existing secrets; otherwise duplicates are skipped.

Response - 200 OK

{ "imported": 3, "skipped": 1, "overwritten": 0 }

Flat (non-enveloped) body.

Errors

Status Code Meaning
400 bad_request Missing file.
400 decrypt_error Invalid password for the user-encryption layer.
400 extract_error Archive could not be extracted (invalid archive).
500 temp_error Could not create temp dir.
500 file_error Could not save the uploaded file.

Side effects

  • Decrypts with openssl (user layer then internal layer), extracts the archive, and runs su - ai -c 'bash vault.sh set ...' for each restored secret.

Example

curl -X POST https://panel.example.com:2087/api/ai/vault/import \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  -F "password=passphrase" \
  -F "overwrite=1"

POST /api/ai/webconsole/disable-lan

Disables LAN access for the OpenClaw web console: reverts the gateway to loopback, removes the nginx proxy, and restarts the gateway.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "success": true, "message": "LAN access disabled" } }

Errors

Status Code Meaning
500 read_failed Could not read openclaw.json.
500 parse_failed Config is not valid JSON.
500 write_failed Could not write openclaw.json.

Side effects

  • Deletes gateway.bind and gateway.controlUi from openclaw.json, writes it, chown ai:ai.
  • Removes /etc/nginx/conf.d/openclaw-proxy.conf and reloads nginx.
  • Restarts the gateway (pkill -u ai openclaw then relaunch).

Example

curl -X POST https://panel.example.com:2087/api/ai/webconsole/disable-lan \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/webconsole/enable-lan

Enables LAN access for the web console: binds the gateway to LAN, relaxes control-UI origin/device-auth, installs an HTTPS nginx proxy on port 18790, and restarts the gateway.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "success": true, "message": "LAN access enabled" } }

Errors

Status Code Meaning
500 read_failed Could not read openclaw.json.
500 parse_failed Config is not valid JSON.
500 write_failed Could not write openclaw.json.

Side effects

  • Sets gateway.bind=lan and gateway.controlUi (allowed origins *, dangerouslyDisableDeviceAuth: true) in openclaw.json, writes it, chown ai:ai.
  • Writes /etc/nginx/conf.d/openclaw-proxy.conf (TLS proxy on 18790 → 127.0.0.1:18789), runs nginx -t, reloads nginx.
  • Restarts the gateway.

Example

curl -X POST https://panel.example.com:2087/api/ai/webconsole/enable-lan \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/webconsole/fix

Auto-fixes the most common console misconfiguration by setting gateway.mode=local in openclaw.json.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "success": true, "message": "Added gateway.mode=local" } }

Errors

Status Code Meaning
500 read_failed Could not read openclaw.json.
500 parse_failed Config is not valid JSON.
500 marshal_failed Could not serialize the updated config.
500 write_failed Could not write openclaw.json.

Side effects

  • Sets gateway.mode=local in openclaw.json, writes it, and chown ai:ai.

Example

curl -X POST https://panel.example.com:2087/api/ai/webconsole/fix \
  -H "Authorization: Bearer $TOKEN"

GET /api/ai/webconsole/healthcheck

Runs a full diagnostic of the OpenClaw web console (install, config, gateway mode/bind, process, port, firewall) and lists any issues.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "openclaw_installed": true, "config_exists": true, "gateway_mode": "local", "gateway_mode_ok": true, "gateway_bind": "lan", "gateway_bind_lan": true, "gateway_token": "<token>", "process_running": true, "port_responding": true, "all_ok": true, "issues": [], "can_auto_fix": false, "client_ip": "203.0.113.10", "firewall_18790_open": true } }

Side effects

  • Read-only checks: LookPath openclaw, reads openclaw.json, pgrep -u ai openclaw, HTTP GET to 127.0.0.1:18789, and firewall-cmd/iptables for port 18790.

Example

curl https://panel.example.com:2087/api/ai/webconsole/healthcheck \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/webconsole/restart

Restarts the OpenClaw gateway process for the ai user and verifies it came back up.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "success": true, "message": "OpenClaw restarted" } }

Errors

Status Code Meaning
500 restart_failed Failed to launch the gateway.
500 not_started Process did not start (no matching pgrep after relaunch).

Side effects

  • pkill -u ai openclaw, then su - ai -c 'nohup openclaw gateway run ...', then verifies via pgrep.

Example

curl -X POST https://panel.example.com:2087/api/ai/webconsole/restart \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/webconsole/start

Starts a web-console proxy session for the caller's IP, returning a per-IP session token.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "success": true, "token": "<hex session token>", "ip": "203.0.113.10" } }

Side effects

  • Stores a random session token keyed by the client IP in an in-memory map (no disk/service change).

Example

curl -X POST https://panel.example.com:2087/api/ai/webconsole/start \
  -H "Authorization: Bearer $TOKEN"

GET /api/ai/webconsole/status

Returns whether the caller's IP has an active web-console session and its token.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "active": true, "token": "<hex session token>", "ip": "203.0.113.10" } }

Example

curl https://panel.example.com:2087/api/ai/webconsole/status \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/webconsole/stop

Ends the web-console session for the caller's IP.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "success": true } }

Side effects

  • Removes the in-memory session entry for the client IP.

Example

curl -X POST https://panel.example.com:2087/api/ai/webconsole/stop \
  -H "Authorization: Bearer $TOKEN"

GET /api/ai/whatsapp/config

Returns the WhatsApp channel config from openclaw.json plus the live connection status from the OpenClaw CLI.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "status": "connected", "phone": "+1555...", "dmPolicy": "allowlist", "groupPolicy": "allowlist", "allowFrom": ["+1555..."], "groupAllowFrom": [], "allowedGroups": [], "requireMention": true } }

Returns { "data": { "status": "disconnected" } } when the config file is missing or unparseable. status/phone come from openclaw channels status whatsapp --json.

Example

curl https://panel.example.com:2087/api/ai/whatsapp/config \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/whatsapp/config

Writes the WhatsApp channel config into openclaw.json.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "dmPolicy": "allowlist", "groupPolicy": "allowlist", "allowFrom": ["+1555..."], "groupAllowFrom": [], "allowedGroups": [], "requireMention": true }
Field Type Required Description
dmPolicy string no Direct-message policy.
groupPolicy string no Group-message policy.
allowFrom array of string no Allowed DM sender numbers.
groupAllowFrom array of string no Allowed group sender numbers.
allowedGroups array of string no Allowed group ids.
requireMention boolean no Whether the bot must be mentioned in groups (stored under the * group).

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 (none) Body failed binding (error.message only).
500 (none) Failed writing openclaw.json (error.message only).

Side effects

  • Merges a channels.whatsapp object into openclaw.json, writes the file, and chown ai:ai.

Example

curl -X POST https://panel.example.com:2087/api/ai/whatsapp/config \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"dmPolicy": "allowlist", "allowFrom": ["+15551234567"], "requireMention": true}'

GET /api/ai/whatsapp/pairings

Lists WhatsApp pairings via the OpenClaw CLI.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "pairings": [] } }

pairings is the JSON array from openclaw pairing list whatsapp --json (empty array on error).

Example

curl https://panel.example.com:2087/api/ai/whatsapp/pairings \
  -H "Authorization: Bearer $TOKEN"

POST /api/ai/whatsapp/pairings/approve

Approves a WhatsApp pairing by code via the OpenClaw CLI.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "code": "ABC123" }
Field Type Required Description
code string no Pairing code (passed directly to the CLI; no binding constraint).

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 (none) Body failed binding (error.message only).
500 (none) Failed to approve pairing when the CLI command fails.

Side effects

  • Runs su - ai -c 'openclaw pairing approve whatsapp <code>'.

Example

curl -X POST https://panel.example.com:2087/api/ai/whatsapp/pairings/approve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "ABC123"}'