Handle Syntax
Secret handles use a simple template syntax embedded in JSON strings.
Format
{{secret:<name>}}
{{and}}are the delimiterssecret:is the literal prefix<name>is the exact name used when registering the secret withash secrets add
Valid Examples
{{secret:openai-key}}
{{secret:db-password}}
{{secret:analytics-api-key-prod}}
Placement in Tool Input
Handles can appear:
- As the entire value of a JSON string field
- As a substring within a JSON string field (embedded in a URL, header value, etc.)
{
"api_key": "{{secret:stripe-key}}",
"url": "https://api.example.com?key={{secret:api-key}}&format=json",
"headers": {
"Authorization": "Bearer {{secret:openai-key}}",
"X-Custom": "prefix-{{secret:token}}-suffix"
}
}
Multiple Handles
A single tool call can reference multiple secrets:
{
"username": "{{secret:db-user}}",
"password": "{{secret:db-password}}"
}
Each handle is resolved independently. All referenced secrets must have matching pre-approval policies.
Handle Resolution Errors
If resolution fails, the tool call returns a ToolFailed error:
| Cause | Error Message |
|---|---|
| Secret not registered | secret 'name' not found |
| Capability missing | agent lacks secret.use:name capability |
| No matching policy | no pre-approval policy matches (name, tool, host) |
| Policy expired | pre-approval policy expired |
| Policy max_uses reached | pre-approval policy exhausted |
Security Notes
- Handles are never logged by
agentd- only the secret name appears in audit entries - The plaintext substitution happens in memory, immediately before calling the tool handler
- The substituted input is discarded after the handler returns
[REDACTED:<name>]in output indicates a secret value was found and scrubbed