Admin HTTP RPC Plugin: Gateway Control Over HTTP

This page covers the bundled admin-http-rpc plugin, which exposes selected Gateway control-plane methods over HTTP for trusted host automation. It is intended for users who cannot sustain an open WebSocket connection to the Gateway.

Read this when

  • Building host tooling that cannot use the Gateway WebSocket RPC client
  • Exposing Gateway admin automation behind a private trusted ingress
  • Auditing the security model for HTTP access to Gateway methods

The bundled admin-http-rpc plugin makes a predefined set of Gateway control-plane methods available over HTTP. It is intended for trusted host automation that cannot sustain an open WebSocket connection to the Gateway.

This plugin is included with OpenClaw but starts inactive. While inactive, no route is registered. Activating it creates POST /api/v1/admin/rpc on the same port as the Gateway (http://<gateway-host>:<port>/api/v1/admin/rpc).

Only turn it on for private host tooling, tailnet scripts, or a trusted internal ingress. Never make this route accessible from the public internet.

Before you enable it

Admin HTTP RPC provides full operator control over the Gateway. Any caller that passes Gateway HTTP authentication can call the listed methods. Activate it only when every condition below holds:

  • The caller is authorized to operate the Gateway.
  • The caller cannot use the WebSocket RPC client.
  • The route is only available on loopback, a tailnet, or a private authenticated ingress.
  • You have verified that the allowed methods match your planned automation.

For OpenClaw clients and interactive tools that can maintain a Gateway WebSocket connection, prefer WebSocket RPC.

Enable

Activate the built-in plugin:

CLI

openclaw plugins enable admin-http-rpc
openclaw gateway restart

Config

{
  plugins: {
    entries: {
      "admin-http-rpc": { enabled: true },
    },
  },
}

The route registers when the plugin starts, so restart the Gateway after modifying the plugin configuration.

Deactivate it when the HTTP surface is no longer needed:

openclaw plugins disable admin-http-rpc
openclaw gateway restart

Verify the route

Send health as the minimal safe request:

curl -sS http://<gateway-host>:<port>/api/v1/admin/rpc \
  -H 'Authorization: Bearer <gateway-token>' \
  -H 'Content-Type: application/json' \
  -d '{"method":"health","params":{}}'

A successful response contains ok: true:

{
  "id": "generated-request-id",
  "ok": true,
  "payload": {
    "status": "ok"
  }
}

When the plugin is off, the route returns 404 because it is not registered.

Authentication

The plugin route uses Gateway HTTP authentication.

Common ways to authenticate:

  • shared-secret auth (gateway.auth.mode="token" or "password"): Authorization: Bearer <token-or-password>
  • trusted identity-bearing HTTP auth (gateway.auth.mode="trusted-proxy"): direct traffic through your identity-aware proxy and let it inject the required identity headers
  • private-ingress open auth (gateway.auth.mode="none"): no auth header needed

Security model

Consider this plugin a full Gateway operator surface.

  • Activating the plugin deliberately exposes the allowlisted admin RPC methods at /api/v1/admin/rpc.
  • The plugin declares the reserved contracts.gatewayMethodDispatch: ["authenticated-request"] manifest contract, which allows its Gateway-authenticated HTTP route to dispatch control-plane methods within the process. This is not a sandbox: the contract only prevents accidental use of reserved SDK helpers, but trusted plugins still run inside the Gateway process.
  • Shared-secret bearer auth (token/password modes) confirms possession of the gateway operator secret; narrower x-openclaw-scopes headers are ignored on that path and normal full operator defaults are restored.
  • Trusted identity-bearing HTTP auth (trusted-proxy mode) honors x-openclaw-scopes when it is present.
  • gateway.auth.mode="none" means this route has no authentication if the plugin is enabled. Use that only behind a private ingress you fully trust.
  • After the plugin route authentication passes, requests go through the same Gateway method handlers and scope checks as WebSocket RPC.
  • The route stays available during a prepared suspension lease. Bounded request validation and the local commands.list discovery response remain functional. Of the methods dispatched into the Gateway, only gateway.suspend.prepare, gateway.suspend.status, and gateway.suspend.resume may run while admission is closed. Other allowlisted methods return the standard retryable Gateway UNAVAILABLE response.
  • Keep this route on loopback, tailnet, or a private trusted ingress. Do not expose it directly to the public internet. Use separate gateways when callers cross trust boundaries.

Request

POST /api/v1/admin/rpc
Authorization: Bearer <gateway-token>
Content-Type: application/json
{
  "id": "optional-request-id",
  "method": "health",
  "params": {}
}

Fields:

  • id (string, optional): echoed back in the response. A UUID is generated if omitted.
  • method (string, required): the name of an allowed Gateway method.
  • params (any, optional): parameters specific to the method.

The default maximum request body size is 1 MB.

Response

Successful responses follow the Gateway RPC format:

{
  "id": "optional-request-id",
  "ok": true,
  "payload": {}
}

Gateway method errors follow:

{
  "id": "optional-request-id",
  "ok": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "bad params"
  }
}

The HTTP status code maps to the error code:

Error codeHTTP status
INVALID_REQUEST400
APPROVAL_NOT_FOUND404
NOT_LINKED, NOT_PAIRED409
UNAVAILABLE503
AGENT_TIMEOUT504
any other code500

Allowed methods

  • discovery: commands.list Provides the HTTP RPC method names that this plugin permits.
  • gateway: health, status, logs.tail, usage.status, usage.cost, gateway.restart.request, gateway.suspend.prepare, gateway.suspend.status, gateway.suspend.resume
  • config: config.get, config.schema, config.schema.lookup, config.set, config.patch, config.apply
  • channels: channels.status, channels.start, channels.stop, channels.logout
  • web: web.login.start, web.login.wait
  • models: models.list, models.authStatus
  • agents: agents.list, agents.create, agents.update, agents.delete
  • approvals: exec.approvals.get, exec.approvals.set, exec.approvals.node.get, exec.approvals.node.set
  • cron: cron.status, cron.list, cron.get, cron.runs, cron.add, cron.update, cron.remove, cron.run
  • devices: device.pair.list, device.pair.approve, device.pair.reject, device.pair.remove
  • nodes: node.list, node.describe, node.pair.list, node.pair.approve, node.pair.reject, node.pair.remove, node.rename
  • tasks: tasks.list, tasks.get, tasks.cancel
  • diagnostics: doctor.memory.status, update.status

Any Gateway methods not listed here stay blocked unless you explicitly add them.

WebSocket comparison

For OpenClaw clients, the standard Gateway WebSocket RPC path is still the recommended control-plane API. Reserve admin HTTP RPC for host tooling that requires an HTTP request/response surface.

WebSocket clients using a shared token without a trusted device identity cannot assign admin scopes to themselves during the connection phase. Admin HTTP RPC intentionally mirrors the existing trusted HTTP operator model: when the plugin is active, shared-secret bearer authentication is granted full operator privileges on this admin surface.

Troubleshooting

404 Not Found

: Either the plugin is off, the Gateway hasn't been restarted after enabling it, or the request targets a different Gateway process.

401 Unauthorized

The Gateway HTTP authentication check was not passed by the request. Verify the bearer token or the trusted-proxy identity headers.

405 Method Not Allowed

A method other than POST was used by the request.

413 Payload Too Large

The 1 MB limit on the request body was exceeded.

400 INVALID_REQUEST

The request body is not valid JSON, the method field is absent, the method is not included in the plugin allowlist, or the suspension resume ID does not correspond to the active lease.

503 UNAVAILABLE

The Gateway method is currently starting, being rate-limited, suspended, or blocked by a competing suspension or resume operation. When present, examine error.details and respect error.retryAfterMs before making another attempt.