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.
Leave at least one allowed host in place. A secret with no allowed hosts is
stored but can never be substituted at egress, so the request appears to
succeed and the credential is unusable. Narrow the list rather than clearing
it.
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.