🏠 Home ← Docs Index
Open Beta 📖 Interactive Reader Raw .md llms.txt

Gateway API Reference

The GoClaw gateway exposes the Web UI, web chat over Server-Sent Events, WebSocket chat, REST administration APIs, webhook ingress, media endpoints, and an OpenAI-compatible chat API.

Routes are registered in cmd/gateway/startup.go (registerAPIRoutes, which also mounts the trace routes of internal/gateway/handler_traces.go) and in cmd/gateway/main.go (the Web UI's files, /canvas/ and /uploads/), and implemented under internal/gateway.

Authentication

Every route on this page needs the gateway token, gateway.auth.token. The gateway does not start without one. These are the exceptions:

Route How it is checked
/health, /healthz, /ready, /readyz Not checked.
The Web UI's own files (/, its scripts, styles and images) Not checked; every API call the page makes sends the token.
/goclaw/hook/… and the channel webhooks (/hook/…) Each checks its sender's own credential. See Webhook receiver.
/goclaw/view/<slug> The page's own view token (?token=<view_token> or an X-View-Token header), or the gateway token.
/api/app/*, /ws/app The App API's own credential. See App API.

Send the token in any of these:

A missing or wrong token is answered 401 {"error":"unauthorized"}. When gateway.auth.token is a ${secret:…} reference the vault cannot resolve, every request is answered 500.

Requests that carry the token are not counted against gateway.rate_limit; neither are health probes and the Web UI's files. When webhooks share the main port (no gateway.ingress.port), gateway.ingress.allow_ips / allow_preset apply to every route on it; loopback is always let through.

Web Chat

POST /api/chat

The Web UI's chat. It runs one message on the default agent as the admin (role system) and streams the reply as Server-Sent Events.

{
  "session_id": "admin-session",
  "content": "Hello",
  "attachments": []
}
Field Notes
session_id Optional; defaults to admin-session.
content The message (message is accepted as an alias). Required unless attachments has an item.
attachments Files uploaded first with /api/upload-workspace or /api/media/upload, each as {media_id, media_url, local_path, filename, mime_type, size}.

A message sent while an earlier one in the same session is still being answered waits for it. If the request ends while it waits, the answer is 409.

The response is text/event-stream. Each frame is event: <name> with a JSON data: line:

Event Data
token content (also delta): the next piece of the reply.
tool_start tool: the tool the agent called.
tool_result tool: the tool that finished.
canvas title, content (HTML): a canvas artifact.
buffer_reset The reply is being retried: discard the text received so far.
error error: what went wrong. No done follows.
done content (the whole reply), attachments (files the agent produced, served under /canvas/), prompt_tokens, output_tokens, elapsed_ms, session_id.

A reset trigger (session.reset_triggers, by default /new and /reset) clears the session, and @config:, @notes:, @sop: directives and /subagents commands are answered without the model; each ends with done, or error when it fails. The directives need an administrator, which this endpoint always is. If the client disconnects, the agent still finishes (for up to 10 minutes) and the reply is saved to the session.

WebSocket

GET /gateway

Upgrades to a WebSocket connection, for chat clients other than the Web UI (the Web UI uses POST /api/chat). When gateway.allowed_origins is set, only those origins may connect.

Message envelope:

{
  "id": "msg-123",
  "kind": "inbound",
  "session_id": "admin-session",
  "payload": {
    "channel": "web",
    "peer_id": "web-tab",
    "content": "Hello"
  },
  "timestamp": "2026-06-20T10:00:00Z"
}

Supported message kinds:

Kind Direction Purpose
inbound client -> gateway User message and optional attachments.
outbound_chunk gateway -> client Streaming token chunk.
outbound gateway -> client Final assistant response.
event gateway -> client Tool lifecycle and buffer-reset events.
canvas gateway -> client Rich canvas artifact update.
error gateway -> client User-facing processing error.

Payloads:

Kind Payload
inbound channel, account_id, peer_id, content, and optional attachments, each {media_id, media_url, local_path, filename, mime_type, size}.
outbound_chunk content: the next piece of the reply.
outbound content, and attachments the agent produced (served under /canvas/).
event {"event_type":"tool_start","tool_name","args"}, {"event_type":"tool_result","tool_name","result","error","elapsed_ms"} or {"event_type":"buffer_reset"} (discard the text received so far).
canvas title, content (HTML).
error error, and ref_id: the id of the message it answers.

Sessions: on channel web, a session_id that starts with admin- is used as given; any other value, or none, gives the connection its own session, admin-conn-<16 hex digits>. Web messages run as the admin (role system). On any other channel the session_id is used as given, and without one the session is p2p:<peer_id>:<default agent>.

A connection's messages are handled one at a time. Reset triggers, directives and /subagents commands work as in POST /api/chat. Off the web channel a message runs with the role GoClaw resolves for its channel and peer_id, and @config:, @notes: and @sop: need a peer paired as admin: anyone else is told so and nothing is changed (the message and the refusal stay in the session, as for any directive).

Core Admin APIs

GET /api/stats

Returns gateway health, provider/model information, session count, connected channels, runtime ingress status, and version metadata.

GET /api/gateway

Returns detailed runtime status, ingress settings, rate-limit state, and recent access logs. Supports ?limit=N.

GET /api/doctor

Runs health checks and returns diagnostic results. With ?stream=1 the results arrive as Server-Sent Events, one check event per check as it finishes and a final done event with {"total", "ok"}.

GET /api/inspect

Returns a combined diagnostic view with sessions, memory snippets, database path, state directory, and server time.

GET|POST /api/config

Reads or mutates active configuration. GET returns the whole configuration keyed as config.yaml is (tools.http.allow), with secrets masked; GET ?key=ai.provider returns a single key. POST supports mutation actions such as set, append, and remove.

GET|POST|DELETE /api/secrets

Manages the encrypted secret vault. Secret values are masked when listed.

GET|POST /api/integrations

Lists, tests, saves and deletes the external services the tools use: GitLab, SonarQube, Grafana, Graylog, NetBox, Uptrace, Zabbix, Wazuh and datasources. GET returns each definition with credentials masked (${secret:…} references are shown as written). POST takes {"action", "type", "name", "config"}:

GET|POST /api/pairing

Lists pending/approved channel pairings and approves, revokes, or updates user roles. GET takes an optional ?channel=. POST takes an action: {"action":"approve","code","role"} approves a pairing code, {"action":"revoke","user_id"} removes a user, and {"action":"update_role","user_id","role"} changes a user's role. Approvals made here take effect in the running gateway at once.

Sessions

GET|POST|DELETE /api/sessions

Session creation is persisted immediately, including empty sessions, so Web UI refreshes do not make newly created sessions disappear.

Each of these changes waits until a message being answered in that session is done, so the reply cannot write the old session back over the change. A request that ends while it waits is answered 503; clear then reports how many sessions it had already deleted.

GET /api/session

Returns paginated message history for one session.

Query parameters:

Parameter Required Notes
id yes Session ID.
limit no Defaults to 50; a value that is not a number from 1 to 500 gives 50.
offset no Offset from the newest message.

The response includes history, parallel meta, total, limit, offset, and username. Web chat attachment previews are stored in meta[].attachments.

Memory and Search

GET /api/memory

Returns memory records grouped for the Web UI.

GET|POST /api/memory/core

Reads or updates an agent's core instruction: ?agent=<id>, the default agent's when left out. The ID is used as given: where the default agent has an ID of its own and no agent is configured as main, ?agent=main reads an instruction stored under main, which no run follows.

GET|POST /api/memory/bootstrap-file

Reads or updates controlled bootstrap files such as AGENTS.md, TOOLS.md, and other admin-managed prompt files, in the workspace of ?agent=<id> (the default agent's when left out).

Memory maintenance endpoints

Endpoint Purpose
/api/memory/replay Replay archived/session memory into the memory engine.
/api/memory/reindex Rebuild memory indexes.
/api/memory/stats Return memory statistics.
/api/memory/prune Prune old or low-value memory.
/api/memory/dedupe Deduplicate memory records.
/api/memory/forget Delete selected memory.
/api/memory/clear Clear memory data.
/api/memory/relearn Trigger relearning workflows.
/api/memory/revise POST {"dry_run"}: weigh each learned fact or preference against older ones on the same subject and retire those it replaces (tagged superseded, not deleted). goclaw memory revise calls it.
/api/memory/revisions GET ?run=&record=&limit=: the recorded decisions that one learned record replaces another, newest first (goclaw memory revisions).
/api/memory/revisions/undo POST {"record":"<memory-id>"} or {"run":"<run-id>"}: restore what a decision retired; an undone decision is not made again (goclaw memory revisions undo).
/api/memory/backfill-metadata Populate missing memory metadata.
/api/memory/notes Read or update agent notes.
/api/memory/export Export memory.
/api/memory/import Import memory.
/api/memory/graph Return knowledge graph data.

GET /api/search

Searches memory and archived material. Common parameters include q and source.

Agents, Tools, and RAG

Endpoint Purpose
GET /api/agents List configured agents and bindings. Each agent carries tool_policy: its effective tool, shell, HTTP and git policy and the keys it overrides.
GET|POST|DELETE /api/skills GET lists installed and built-in skills. POST takes an action: install, toggle (turn a skill on or off), verify or save. DELETE ?name=<skill> removes a user-installed skill.
GET|POST /api/skills/quality A skill's quality score: GET ?name=<skill> returns the cached score or evaluates it; POST {"name","force":true} evaluates it again.
GET /api/tools List registered tools.
POST /api/rag-test {"query"}: show which tools and skills Tool RAG and Skill RAG would select for it, with their scores.
GET|POST /api/mcp GET lists the configured MCP servers. POST takes an action: test connects a draft definition and lists its tools, call runs one tool, save writes the definition into config.yaml, delete removes it.

Automation

Endpoint Purpose
/api/cron GET lists cron jobs with their next run. POST {"action","id"} with run, enable, disable, update (with "schedule") or delete; a system job (sys-…) cannot be deleted, only disabled. Jobs are created with the agent's cron tool; this API has no create.
/api/sop Manage SOP definitions. GET lists them, or with ?id= returns one with cron_schedule, its cron job's schedule. POST saves one: on an existing SOP, a field the body leaves out (top level, or in a step with the same id) keeps its stored value, and a changed definition archives the previous version. A cron_schedule in the body creates or reschedules the SOP's cron job; one that cannot be applied is returned as warning. DELETE ?id= removes one.
/api/sop/executions List SOP executions.
/api/sop/run POST {"id"}: start a run of an SOP in the background; the answer has its execution_id. A paused SOP, or one overdue for review, is answered 409 with the reason.
/api/sop/approve POST {"job_id","action":"approve"|"reject","reason"}: approve or reject an SOP step that is waiting for approval. A step nobody decides fails after 1 hour.
/api/sop/validate Validate an SOP definition.
/api/sop/channels Pin SOP notification channels.
/api/capability-requests GET lists capability requests; ?status= filters them.
/api/capability-requests/resolve POST {"id","status","resolved_by","note"}, with status resolved or rejected.

Webhooks and Plugins

Endpoint Purpose
POST /api/webhook Generic inbound message endpoint. See POST /api/webhook.
GET /api/hooks Registered gateway hook events (built-in and Go extensions) with their handlers. The Hooks page's rules are /api/hooks/rules (see Other Routes).
GET|POST /api/webhooks List webhook routes (GET); create, update, enable, disable or delete a stored route (POST with action). See Webhook routes API.
GET|DELETE /api/plugins GET lists the installed agent plugins (the Plugins page); DELETE ?name=<plugin> turns one off for every agent and removes it.
GET /api/extensions Discovered Go/OpenClaw extensions (extensions/ directories) and which are active.
GET /api/audit Query webhook audit logs: ?channel=, ?event=, ?user=, ?limit= (default 100, at most 500). With ?kind=admin, the administrative trail (SOP deletes, channel pins) instead: ?action=, ?actor=, ?resource=, ?limit=.
POST /goclaw/hook/<type>[/<key>] The webhook receiver outside systems (GitLab, GitHub, Graylog, Zabbix, …) deliver events to. See Webhook receiver.

When the Teams or Facebook channel, or the Telegram channel in webhook mode, is enabled, its webhook is mounted on the webhook listener at /hook/msteams, /hook/facebook or /hook/telegram, or at /hook/<webhook_secret_slug>/<channel> when gateway.webhook_secret_slug is set.

POST /api/webhook

Runs one message on the default agent and answers with the whole reply. It needs the gateway token on every port, a separate gateway.ingress.port included:

{"channel": "discord", "account_id": "discord", "peer_id": "u_123456", "content": "Hello"}
{"content": "Hi! How can I help?"}

The session is p2p:<peer_id>:<default agent>, and the message runs with the role GoClaw resolves for that channel and peer_id, as for a message from that channel, or, when account_id is cron:<user ID> (as cron agent jobs send it), with that user's role. With "channel": "web" it runs in admin-session as the admin (role system). With "subject_ref": "<user ID>" (a cron agent … job with a subject sends it) the message runs as that person instead, at role user with a delegation, on every channel, web included: account_id, the peer's pairing and memory.write_access do not set its role, so directives are refused. Its session is p2p:<peer_id>:<default agent>:for:<user ID>. goclaw message <channel> <text> posts here. A message sent while an earlier one in the same session is still being answered waits for it; if the request ends while it waits, the answer is 409. Reset triggers, directives and /subagents commands work as in POST /api/chat, except that off the web channel @config:, @notes: and @sop: need that role to be admin. goclaw message <channel> sends as peer cli, which is paired with no one, so a directive goes through it only on web (the default). An agent error is answered 500 with the error text.

Webhook routes API

/api/webhooks needs the gateway token (X-Goclaw-Token: <token> or Authorization: Bearer <token>).

GET lists every route: the stored ones, and the config.yaml ones with "source": "config" and "id": 0, which cannot be changed here. Secrets are masked; secret_set says whether the route has a secret or token at all. hit_count, last_hit_at and last_error are kept for stored routes only. last_error is the latest refusal or drop (its time, status and why: a delivery refused, or taken and then dropped unrun); the next delivery that gets through clears it.

POST takes a JSON body with an action:

curl -X POST https://<gateway>/api/webhooks \
  -H "Authorization: Bearer $GOCLAW_TOKEN" -H "Content-Type: application/json" \
  -d '{"action":"create","type":"graylog","key":"payments-errors","agent":"main","sop_id":"incident-triage"}'
{"ok": true, "id": 7, "agent": "main", "generated_secret": "whsec_…"}
Field Actions Meaning
action all create, update, enable, disable or delete.
id update, enable, disable, delete The stored route's id from GET.
type create (required), update First path segment of /goclaw/hook/<type>/<key>. On update, empty keeps the stored type.
key create, update Second path segment. Empty makes the type's route for any key that has no route of its own.
agent create, update (required) Agent ID or name, any case, or * for every agent. An unknown agent is refused with 400, naming the agents there are.
secret, token create, update On create, leave both out to have a whsec_… secret generated: it is returned once, as generated_secret, and not shown again. On update, an empty value keeps the stored one.
events create, update Comma-separated event names, e.g. "push,merge_request"; empty takes every event.
sop_id, batch_mode, batch_window create, update Run a SOP per event, or per batch. On update, an empty batch_mode or a batch_window of 0 keeps the stored value.

The fields mean what they mean in config.yaml: see Webhook Routes.

Webhook receiver

POST /goclaw/hook/<type>[/<key>] is where outside systems deliver events. It is served on the webhook listener: gateway.ingress.port when that is set, else the main port. The route is the one for this type and exact key, else the type's route without a key; between a stored route and a config.yaml route for the same key, the stored one. How each type authenticates (GitLab token, GitHub HMAC, otherwise X-Token, ?token= or an X-Signature HMAC) is in Webhook Routes.

Status When
200 {"status":"ok", …} The event was taken: a SOP run started (or the event queued, for a bulk route or behind the run in progress for the same object), or the agent will answer it. dispatched counts the agents it reached.
200 {"status":"filtered"} The route's events leave this event out. Nothing runs.
200 {"status":"duplicate"} The route took a delivery with this ID before (a retry, a redelivery). Nothing runs again.
200 {"status":"test"} Graylog's test notification, on a graylog route. Nothing runs.
401 The credential is missing or does not match, or the route has none; the body says which.
403 The sender's IP is not on gateway.ingress.allow_ips / allow_preset (only when an allowlist is set; loopback is always let through).
404 No route for the type and key, or the route is paused.
409 The route's SOP cannot run: it was deleted or disabled, or it is overdue for review.
413 The body is larger than 4 MB.
429 The sender's IP is over gateway.rate_limit.
500 The event was not taken, for example the SOP run could not be started. Send it again.
503 Too many deliveries are in progress, or 500 events already wait for the same object's run (Retry-After: 30), or another request is taking this same delivery right now (Retry-After: 10); sent again, it is a repeat or it runs. Also when the delivery's ID could not be recorded (Retry-After: 10): run unrecorded, a resend could not be told from it.

A refused delivery to a stored route is noted on the route (last_error, and on the Webhooks page); for a config.yaml route, only the gateway log has it ([webhook] lines). The 503 for a delivery another request is taking is only in the gateway log: sent again, that delivery is a repeat or runs, as the other request ended.

Media and Canvas

POST /api/media/upload

Uploads a file, sent as the multipart form field file, to the media store (<state dir>/media) and returns an ID, URL, filename, MIME type, size, and creation time.

GET|HEAD /media/<id>

Serves a media-store object. Images, audio, video, PDF and plain text are served inline with their stored content type; anything else (HTML, SVG, XML, scripts, unknown types) is served as a download (application/octet-stream).

POST /api/upload-workspace

Uploads a file, sent as the multipart form field file, directly into workspace/uploads. Web chat uses this endpoint so tools can access uploaded files by workspace-relative path.

The response includes:

{
  "id": "timestamp-filename.png",
  "local_path": "uploads/timestamp-filename.png",
  "filename": "filename.png",
  "url": "/uploads/timestamp-filename.png",
  "mime_type": "image/png",
  "size": 12345
}

GET /uploads/<file>

Serves files from workspace/uploads. Images, audio, video, PDF, .txt and .log files are served inline; any other file, or one without an extension, is served as a download.

GET /api/canvas

Lists canvas artifacts, each as {name, size, mod_time}.

GET /canvas/<file>

Serves generated canvas artifacts from <state dir>/canvas, inline or as a download by the same rule as /uploads/. Artifacts are removed after 48 hours.

GET /goclaw/view/<slug>

Serves an HTML page the agent made (generate_html). It needs the page's view token (?token=<view_token> or an X-View-Token header) or the gateway token. /goclaw/view/ without a slug lists the pages and needs the gateway token.

The page is served sandboxed (Content-Security-Policy: sandbox allow-scripts …, without allow-same-origin), so the browser treats it as an origin of its own: its scripts run, but no dashboard cookie goes with its requests, and it cannot post forms, open frames, use fetch, or read localStorage or document.cookie.

Messaging

POST /api/send

Sends a message to a connected channel, without the agent:

{"channel": "discord", "target": "<channel or user ID>", "text": "Hello"}

All three fields are required. It needs the gateway token and accepts only requests from a loopback address (127.0.0.1, ::1); any other is answered 403. A channel that is not connected is answered 404, with the connected channels in available.

Token Metrics

GET /api/metrics/tokens

Returns token usage summaries. Optional filters include agent_id, channel, provider, model, date_from, date_to, and limit (1 to 500, default 50).

Traces and Logs

Endpoint Purpose
GET /api/traces Agent traces, newest first. Filters: session_id, agent_id, channel, status, tag, sop_id, from and to (unix seconds, RFC 3339 or YYYY-MM-DD), min_tokens (prompt and output together), before (unix seconds, for paging) and limit (default 50, at most 200). sort=tokens lists the traces that used the most tokens first, paged by offset instead of before. A SOP run's trace is filed under the agent that ran it; sop_id finds a SOP's runs. A scheduled built-in or shell command, or a schedule whose SOP is gone, has no agent: find its job's runs by session_id (cron:<job id>); GET /api/traces/<id> also gives the job as cron_id.
GET /api/traces/stats Trace counts (total, done, error) and averages of duration, tool calls and tokens.
GET /api/traces/<id> One trace with its steps, tool calls and timings.
GET /api/traces/<id>.csv The same trace as a CSV download.
POST /api/traces/<id>/feedback {"score": 1 or -1, "comment", "user_id"}: rate a trace.
GET /api/logs The gateway log as Server-Sent Events, one line per event.

Other Routes

Endpoint Purpose
GET /api/sbom, GET /api/admin/sbom The software bill of materials: ?format=cyclonedx (default), spdx or summary; ?download=1 sends it as a file. See SBOM.
Web UI only: /api/agents/profiles, /api/agents/profiles/draft, /api/agents/instructions, /api/agents/instructions/draft, /api/agents/persona/draft, /api/agents/constraints, /api/agents/preview, /api/hooks/rules, /api/plugins/{inspect,install,agents,approve,inputs,fetch,install-fetched,marketplaces,marketplaces/browse,check-updates,export}, /api/memory/wiki and /api/memory/wiki/{history,restore,distill,attachments,search} The Agents, Instructions, Hooks, Plugins and Wiki pages of the Web UI. Their request shapes follow those pages and are not described here. They need the gateway token like the rest.

App API

POST /api/app/chat and GET /api/app/events serve the Enterprise App API channel. They check the app's own credential, not the gateway token, and /ws/app answers 410 Gone. See App API.

OpenAI Compatibility

POST /v1/chat/completions

OpenAI-compatible chat completions endpoint. See OpenAI Compatibility.

Health Probes

GET /health, GET /healthz, GET /ready, GET /readyz

Lightweight health probes that return {"status":"ok","uptime_seconds":…}. These endpoints are intentionally unauthenticated. When gateway.ingress.port gives webhooks their own listener, they are served there too.