A secret is a named value stored encrypted and read only by the runtime. Tools, integrations and carrier accounts reference it as {{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 in secrets 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 agent’s tools read it like any secret:
  • 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; secrets on 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 secrets your 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:
In a prompt or greeting the error reads:
In a code operation, only the secrets the tool names are sent to the sandbox:
A string that is exactly one token resolves to the stored value as-is; a token inside a longer string is substituted as text:

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 secrets is refused with 400, 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, and tool.failed fires.
A value an endpoint echoes back in its response reaches the model like any other response field. Do not call endpoints that reflect credentials.

Deleting a secret

DELETE /v1/secrets/{secret_id}, ADMIN only. A secret still referenced is refused with 409, naming each user:
References that block deletion: a tool’s draft or published version; an older tool version still pinned by a published agent or task (republish that agent too, see the pin rule); an integration or carrier account. Tool versions nothing runs do not block.

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.