2026-07-20

OpenClaw HTTP API: Invoke Agent Tools Safely

Use the OpenClaw HTTP API safely with private Gateway access, operator authentication, agent and session routing, tool policies, idempotent retries, and response validation.

OpenClaw HTTP API: Invoke Agent Tools Safely cover illustration

Five calls on 23 July 2026 exposed the useful boundary in OpenClaw's HTTP API. Missing credentials stopped at 401. A bearer-authenticated sessions_list call returned 200. Sending the same shared secret with a deliberately narrower x-openclaw-scopes: operator.read header still returned 200, matching the documented full-operator semantics of shared-secret HTTP auth. The default HTTP deny list hid exec behind 404, while an invalid limit: 0 failed schema validation with 400.

Those results make the OpenClaw HTTP API easier to reason about. POST /tools/invoke is a synchronous adapter around one effective tool call, not a narrow application-key system and not a generic remote-control socket. The caller crosses the Gateway's operator credential boundary, agent and session routing, effective tool policy, the HTTP-specific deny list, and the selected tool's schema before any result is useful.

The safe integration is deliberately small: a private route, one protected operator credential held by a server-side adapter, a fixed tool with a narrow schema, explicit routing only where the use case needs it, and response handling that refuses to infer more than the returned evidence proves.

A Gateway bearer token is an operator secret, not an application API key with narrow scopes. In token or password mode, supplying a smaller x-openclaw-scopes header does not reduce the shared-secret caller's authority. If two services should not trust each other, separate gateways are a cleaner boundary than creative headers.

The useful surprise was the 404

The fresh probe targeted the running Gateway over its local loopback address on 127.0.0.1:18789. It attempted no write tool and changed no configuration. A request without an Authorization header stopped at authentication:

POST /tools/invoke
Content-Type: application/json

{"tool":"sessions_list","args":{"limit":1}}

HTTP/1.1 401 Unauthorized
{"error":{"message":"Unauthorized","type":"unauthorized"}}

After adding the configured bearer token, the same tool returned 200 with an ok: true envelope and the expected content and details result fields. The evidence record deliberately omits session values: a successful call proves only that this tool ran inside its effective context, not that the caller saw a global inventory.

A third call kept the same shared secret but sent x-openclaw-scopes: operator.read. It still returned 200. That is not a privilege-escalation bug; current OpenClaw documentation says token and password authentication on this HTTP surface restores the full default operator scope set and ignores a narrower declared scope header. Services that need real trust separation need separate Gateways or an identity-bearing boundary, not a decorative header.

The more important negative test asked for exec with harmless arguments. Authentication succeeded, but the Gateway answered:

HTTP/1.1 404 Not Found
{"ok":false,"error":{
  "type":"not_found",
  "message":"Tool not available: exec"
}}

That is the desired result. OpenClaw's HTTP layer hard-denies command execution, arbitrary file mutation, cross-session injection, cron control, Gateway control, node relay, and agent spawning unless an owner deliberately changes the exposure override. Returning 404 also avoids turning the endpoint into a convenient catalog of blocked capabilities.

A request capsule passing through credential, agent and session routing, and tool-policy modules before reaching one approved tool
A valid credential opens the first gate. Routing and effective tool policy still decide whether a named tool exists for this request.

Failure modes across the five gates

/tools/invoke is always registered on the same multiplexed port as the Gateway. That does not make every OpenClaw tool reachable. Availability is calculated through the normal policy chain: global and provider profiles, global allowlists, agent-specific rules, group policy when the session maps to a channel, and subagent policy for subagent sessions. The HTTP-specific deny list is then applied on top.

BoundaryQuestion it answersTypical evidence
NetworkCan the caller reach the Gateway listener?Private route succeeds; public route does not exist
Gateway authDoes the caller hold the configured operator credential or arrive through a trusted identity-bearing proxy?401 without valid auth; request proceeds with valid auth
RoutingWhich agent and session context should shape the call?Resolved session matches the requested agent, or 400 on conflict
Effective policyIs this tool available inside that context?200 for an allowed tool; 404 when unavailable
Tool executionAre the arguments valid, and did the tool finish successfully?ok envelope plus a validated tool result

Exec approvals do not add another per-request consent dialog here. They are operator guardrails elsewhere in the system, not an authorization boundary layered on top of this endpoint. If an administrator removes exec from the HTTP deny list for an owner caller, that caller has gained a remote shell surface. Denying write or apply_patch at the same time would not make the shell read-only.

Keep the default list intact unless a concrete integration cannot exist without an override. A custom plugin tool that exposes one validated business operation is usually easier to reason about than a broad core primitive.

Session keys route context; they do not authenticate people

The request may include sessionKey, agentId, both, or neither. Omitting them uses the configured main-session behavior. Supplying agentId asks the Gateway to resolve that agent's session. When an explicit session already maps to a different agent, combining the two produces a 400 rather than silently switching identities.

This is a routing contract, not a tenant boundary. A session key chooses context for policy and tool behavior; it is not a password and should never be accepted from an untrusted user as proof that the user owns that session. The same caution applies to the optional message-context headers:

  • x-openclaw-message-channel identifies a channel such as Telegram or Slack for group-policy evaluation.
  • x-openclaw-account-id, x-openclaw-message-to, and x-openclaw-thread-id supply delivery context where a message tool needs it.
  • None of those headers replaces Gateway authentication.

Prefer a fixed server-side routing choice. If the application truly needs several agents or sessions, validate the allowed set before creating the request. Passing arbitrary client input through to sessionKey turns a small integration into an accidental context browser.

Freeze a request contract smaller than the endpoint

The endpoint accepts a tool name, an optional action, an args object, optional agent/session routing, an optional idempotency key, and a currently ignored dryRun field. Its default request limit is 2 MB. Most applications should expose far less to their own callers.

A service that only needs a bounded status lookup can fix the tool and routing in code:

curl --fail-with-body http://127.0.0.1:18789/tools/invoke \
  -H "Authorization: Bearer $OPENCLAW_GATEWAY_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "tool": "sessions_list",
    "args": {"limit": 20},
    "agentId": "main",
    "idempotencyKey": "status-window-2026-07-21T03:00Z"
  }'

Do not let a public request choose tool, raw args, or routing fields. Translate an application-level operation into a fixed OpenClaw call, validate values against the tool schema, cap body size well below the Gateway limit, and log a correlation identifier without logging the Authorization header or sensitive tool output.

The optional top-level action is merged into args.action only when that schema supports an action property and the arguments did not already define it. Sending both is needless ambiguity. Pick one representation in the client library and test the serialized body.

An idempotency key is an address, not a transaction

idempotencyKey gives the invocation a stable tool-call identifier. That is valuable for correlation and for tools that implement replay protection around that identity. It does not magically make an arbitrary external side effect exactly-once.

Consider a custom tool that creates a ticket. The network can fail after the ticket service commits but before the caller receives the HTTP response. Retrying with the same OpenClaw idempotency key produces a stable call identity, yet the ticket tool still needs its own durable deduplication rule—usually a unique business key stored next to the created record.

Classify operations before adding automatic retries:

  • Pure reads can normally retry with bounded backoff.
  • Idempotent writes can retry when the downstream system enforces the same key.
  • Non-idempotent actions need reconciliation after an ambiguous timeout, not blind repetition.

The dryRun field is reserved and currently ignored. It is not a safety preview. A tool must define its own non-mutating preview semantics if the application needs them.

Treat the response as two envelopes

First validate HTTP transport. Then validate the OpenClaw result. A 200 response has the shape {"ok":true,"result":...}, but the inner tool result still belongs to that tool and may contain an empty collection, warnings, partial visibility, or domain-specific status. The live sessions_list probe is a good example: transport and invocation succeeded even though the visible result set was empty.

Failure statuses are deliberately specific. 400 covers malformed requests, bad arguments, and agent/session conflicts. 401 means authentication failed; repeated failures may become 429 with Retry-After. 403 represents a policy refusal that may include requiresApproval. 404 means the tool is unavailable or not allowlisted. 408 and 413 cover body-read timeout and size. 500 is a sanitized unexpected execution failure.

A modular response-validation system with one completed result path and several safely disconnected failure paths
Transport success, invocation success, and an acceptable domain result are three separate checks. Preserve that separation in the client.

A useful client error should record the HTTP status, OpenClaw error type, correlation or idempotency key, selected tool, and a redacted summary. It should not flatten every failure into “Gateway unavailable,” and it should never write the bearer token or unrestricted tool output into routine logs.

A production shape that stays honest

The strongest deployment pattern is a private application adapter beside the Gateway. It owns the operator credential, exposes a small application-specific API, maps each operation to one fixed OpenClaw tool, and returns a deliberately reduced result. External callers never see the Gateway token or the generic tool surface.

  1. Bind the Gateway to loopback, tailnet, VPN, or another controlled private ingress. Do not place /tools/invoke directly on the public internet.
  2. Keep token or password material in a secret store and inject it into the adapter process. Avoid command-line arguments, source files, browser storage, and request logs.
  3. Allow only tools required by the integration. Add HTTP-specific denies for powerful tools that the agent itself may still need interactively.
  4. Fix or validate agent and session routing server-side. Treat caller-supplied message context as data to validate, not authority.
  5. Set short connect and request timeouts, bounded concurrency, and retry rules based on the operation's side effects.
  6. Alert on repeated 401, 403, 404, and 429 responses. They often reveal credential drift, policy drift, a renamed tool, or an integration probing beyond its contract.

Use webhooks instead when an outside event should enqueue context or start an asynchronous agent run. Use the Gateway protocol when a trusted operator client needs streaming events, presence, or a longer-lived control session. Use cron when OpenClaw should initiate work on a schedule. /tools/invoke earns its place when one trusted service needs one bounded tool result now.

Five calls at the operator boundary

The 23 July rerun on OpenClaw 2026.7.1-2 (0790d9f) proves five current behaviors on this Gateway: missing credentials stop at 401; an allowed read tool can return 200; a narrower scope header does not demote shared-secret authority; exec remains unavailable over HTTP by default with 404; and invalid tool arguments fail with 400. Together, those outcomes separate authentication, declared scopes, availability, and schema validation without invoking a mutating tool.

The rerun does not prove that every plugin tool is safe, that a custom write is idempotent, that another agent has the same effective policy, or that a public reverse proxy preserves the intended auth boundary. It did not test password, trusted-proxy, or open private-ingress modes. Those claims need their own artifacts; turning on a dangerous HTTP exposure override merely to make a generic integration demo pass would invalidate the exercise.

If you want an application to call OpenClaw directly, start with the negative tests: no credential, wrong route, unavailable tool, bad arguments, ambiguous timeout. The happy path becomes trustworthy only after those failures stop where you expect.

The decision rule is narrower than the endpoint: expose one application operation, map it to one fixed tool call, and keep the operator credential behind that adapter. If a caller needs arbitrary tools, arbitrary routing, or public reachability, the integration is no longer a small HTTP convenience; it is an operator control plane and should be isolated accordingly. Related operator notes cover OpenClaw tool policy, command approvals, and webhook design.

Validation record and current sources