Tools

Secrets

secrets lets the agent ask you for a credential without ever seeing it. The agent names an entry, you type the value into a trusted prompt, and the Gateway writes it straight into the shared secret store. The value never appears in the chat, the session transcript, the tool result, or the model's context — the agent only learns that the entry now exists.

The tool is available only in the main session. Subagents and other non-primary runs do not receive it.

It is enabled by default and governed by the normal tool policy — there is no dedicated config key. To remove it, deny it like any other tool (for example tools.deny: ["secrets"] in openclaw.json); allowlists and tool profiles apply to it the same way.

Actions

  • request — ask the human for a credential and store it under a name such as STRIPE_API_KEY. Requests are protected-secret only: an env value is readable through list, so requesting one would break the promise the masked prompt makes. The agent may propose allowedHosts and a short reason shown on the prompt, and the tool blocks until you answer, skip, or it times out (15 minutes by default). The request is bound to the requesting agent run; if that run ends before you answer, the write is refused.
  • list — entry metadata: name, kind, allowed hosts, and last update. Secret values are structurally absent from the listing. Operator-set env entries show their value, since those are injected into exec environments anyway and are agent-readable by design.
  • delete — soft-delete an entry by name. Deleted entries are purged after 30 days.

There is deliberately no action that writes a value the agent supplies. If a value must enter the store, it arrives through the human prompt, the /settings/secrets page, or the openclaw secrets store CLI.

Answering a request

The web Control UI docks the prompt above the composer with a masked input. The prompt always shows who is asking (agent and session), the entry name, the agent's stated reason, and an editable list of allowed hosts, so you have the final say on where the credential may be used. If the name already exists, the prompt says so and shows when and by whom the entry was last updated; submitting replaces the stored value.

Skipping the prompt, or letting it expire, tells the agent that no credential arrived; it continues with best judgment rather than failing the turn.

iOS, macOS, and Android render the same card with a masked secret field.

Chat channels never accept the value. On Telegram, Discord, and similar surfaces the request is delivered as a link to the Control UI prompt — typing a credential into a chat message is exactly what this flow exists to avoid, so a plain-text reply is not captured as an answer.

Creating a credential request requires an operator.admin client (the agent's own Gateway client qualifies) and is bound to the requesting agent run. Answering needs only the normal question scope, because answering provides a value rather than reading one.

Using a stored credential

A stored entry is a regular shared-store entry (see Secrets management):

  • Reference it from config as { "source": "store", "id": "STRIPE_API_KEY" } wherever a SecretRef is accepted (provider API keys, channel tokens). Writes refresh affected config references automatically.
  • env entries are injected into gateway-host exec environments starting with the next agent run.
  • secret entries are substituted into subprocess traffic only when the egress proxy is enabled (secrets.egressProxy.enabled) and the destination matches the entry's allowed hosts; the agent and its subprocesses otherwise see only an opaque placeholder.
Was this useful?
On this page

On this page