{{secrets.NAME}}; the value is never returned to you, the model or the caller. Every API key, bearer token, signing key or password a tool sends belongs here, never in a prompt, vars or userdata, which the model can see and repeat.
There are two sources, read through the one namespace:
Storing a secret
ADMIN only. Dashboard: Build → Secrets. API:POST /v1/secrets, which is an upsert on name.
Rotating is storing the same name again. The old value is gone immediately (no versions, no undo), and every reference uses the new value from its next run with no republish.
Reading secrets
GET /v1/secrets is readable by every member and returns names only, never values:
value_hint is the last four characters. Nothing can reveal a stored value; if you lose the original, store a new one.
Session secrets
A per-session credential — a callback token you mint for one interview, one customer, one call — goes insecrets on the request that starts the session.
POST /v1/calls/token, POST /v1/calls/outbound, POST /v1/chats,
POST /v1/calls/{session_id}/resume and a tool test run
(POST /v1/tools/{tool_id}/run) take it. Batches and task runs do not; their tools see workspace secrets only.
- The values are never shown to the model, never returned by any endpoint and never put in the browser token; they are stored encrypted with the session.
- They reach every agent the session runs: team members, handoff targets and entered tasks.
- They are deleted with the session’s content — by your retention policy or
DELETE /v1/calls/{session_id}/DELETE /v1/chats/{chat_id}. - A resumed call keeps the dropped call’s secrets;
secretson the resume request adds names or replaces values (a rotated short-lived token). - Inbound calls and chats have no starting request: they see workspace secrets, plus any
secretsyour inbound webhook answers with.
Where {{secrets.NAME}} resolves
A secret can only be sent by an http operation or read by a code
operation. Every other operation speaks, shows, stores or compares what it
resolves, so a secret there is refused at save.
In an operation the error reads:
code operation, only the secrets the tool names are sent to the sandbox:
A missing secret fails loudly
A reference never resolves to an empty string.- At save and publish: a tool reading a name the workspace does not have is a warning — every session must supply it in
secrets, and an inbound call or chat will fail the tool. An inline MCP server, integration or carrier account referencing a missing name is an error. - At session start: a request whose agents’ tools read a name that is neither a workspace secret nor in its
secretsis refused with400,this session needs secrets it was not given, one line per name:tool 'book' reads {{secrets.TOOL_TOKEN}}, which is not a workspace secret and was not supplied. Handoff targets outside the session’s team are not checked here. - At runtime: if a tool meets a missing secret anyway, it fails before any operation runs with
secret 'NAME' is not a workspace secret and was not supplied for this session, the model gets the standard tool-failure message, andtool.failedfires.
Deleting a secret
DELETE /v1/secrets/{secret_id}, ADMIN only. A secret still referenced is refused with 409, naming each user:
Plaintext in integrations and carrier accounts
Integration and carrier-account credential fields accept either a reference ({{secrets.RESEND_API_KEY}}, which must exist) or raw plaintext. Plaintext is stored as a new secret under a default name such as RESEND_API_KEY, or a suffixed name like RESEND_API_KEY_a91c if that name is taken, and only the reference is kept on the integration. Existing secrets are never overwritten this way.
Next
Templating and data
The six roots a tool operation can read.
Provider keys
The separate store for the model keys your agents run on.