Agent secrets
An agent secret is a named, encrypted credential stored on a specific agent. Tools reach it from argument_override via {"$ref": "agent.secrets.<name>"}, the value is passed to the tool at execution time, and it is masked everywhere the agent or its session events are read back.
Use secrets for the credentials a tool needs to call out: a Jira API token, a Slack webhook URL, a database password the agent reaches through web_get. Keep agent.metadata and argument_override for non-sensitive configuration. Both of those are returned verbatim by GET /v2/agents/{agent_key}; secrets are not.
Why not put credentials in metadata or argument_override?
agent.metadata is a free-form map intended for organizing agents and for use as $ref values inside tool configurations. It is returned in plaintext by GET /v2/agents/{agent_key}, and the response is visible to anyone with read access on the agent. The same applies to literal values written into argument_override — the agent definition stores them, and reads return them.
Secrets fix both problems. They live on a separate resource (/v2/agents/{agent_key}/secrets), are encrypted at rest with the agent's encryption key, are never returned in plaintext on read, and are masked in the tool_input events of every session that uses them.
Endpoints
All three endpoints require owner, administrator, or agent_administrator (API role), or agent_administrator (agent role).
| Method | Path | Effect |
|---|---|---|
GET | /v2/agents/{agent_key}/secrets | Returns the agent's secrets with values masked as "****". |
PUT | /v2/agents/{agent_key}/secrets | Replaces the whole set. Names not in the request are removed. |
PATCH | /v2/agents/{agent_key}/secrets | Partial update. Names mapped to a value are added or replaced. Names mapped to null are removed. Names absent from the map are left alone. |
The response from every endpoint is the same AgentSecrets shape with masked values. Plaintext is never on the wire after a write completes.
Set secrets
Use PUT to write the full set in one call. This is the right verb for first-time setup and for any flow that owns the entire secrets list.
REPLACE THE SECRETS SET
Code example with multiple language options.1
Use PATCH to add, rotate, or remove individual secrets without resending the rest. A name mapped to null removes it; a name absent from the map is left as it was.
ROTATE ONE SECRET, REMOVE ANOTHER, LEAVE THE REST ALONE
Code example with bash syntax.1
Per-value size cap is 4096 bytes. Secret names are arbitrary map keys; pick something stable, because tool configurations reference them by name.
Reference a secret from a tool
Inside argument_override, replace the literal credential with a $ref to the secret you just wrote. The platform substitutes the plaintext value when the tool runs. The tool receives the real credential; everything observed from outside the tool sees the mask.
A WEB_GET TOOL WRAPPING JIRA, WITH THE API TOKEN REFERENCED FROM SECRETS
Code example with json syntax.1
$ref accepts the same path grammar everywhere secrets are valid: top-level string fields, nested headers, body fields, array elements. The substitution happens once per tool invocation.
If a tool references agent.secrets.<name> and that name is not currently set, the reference fails at execution time and surfaces as an error event on the session. There is no validation at PUT or PATCH time that every existing reference resolves — the rule is checked when the tool runs.
How values are protected
Three layers, each closing a different hole.
- At rest. Secrets are stored encrypted, using the same agent encryption key that protects service-account credentials and connector tokens.
- On read.
GET /v2/agents/{agent_key}/secretsand the response fromPUT/PATCHalways return"****"in place of every value. The plaintext leaves the platform only when a tool that references the secret is executed. - In session events. The platform emits a
tool_inputevent for every tool call so that session viewers and clients can see what the agent did. Fields populated fromagent.secrets.<name>are replaced with"****"in that event. The same call's tool receives the real value; the event records the mask.
The threat model is database compromise and accidental exposure through API responses or the session-event stream. Secrets are not protected from a custom tool that chooses to log its own arguments — once the tool receives the plaintext, what it does with it is the tool's responsibility.
List secret names
GET returns the keys without revealing the values, so it doubles as a way to audit what is configured without leaking anything.
LIST THE SECRET NAMES ON AN AGENT
Code example with multiple language options.1
Remove secrets
To remove a single secret, PATCH with the name mapped to null. To clear every secret on the agent, PUT an empty map.
CLEAR ALL SECRETS
Code example with bash syntax.1
Removing a secret that a tool's argument_override still references does not delete the reference. The next tool call that tries to resolve it produces an error event on the session, so plan rotations either as PATCH overwrites (write the new value under the same name) or by updating the tool configuration first.
Migrating from metadata or argument_override
Existing agents that store credentials in agent.metadata or as literal values in argument_override keep working. To move them onto secrets:
PUTthe credential to/v2/agents/{agent_key}/secretsunder a stable name (jira_api_token,slack_webhook).PATCHthe agent in a single request: replace the literal in the tool'sargument_overridewith{"$ref": "agent.secrets.jira_api_token"}, and remove the credential frommetadata. After thisPATCH,GET /v2/agents/{agent_key}no longer returns the plaintext.