{{secrets.NAME}}; nothing ever hands the value back to you, to the model, or to the caller.
This is where every API key, bearer token, webhook signing key and carrier password your agents need belongs.
Storing one
ADMIN only. In the dashboard it is Build → Secrets; over the API it isPOST /v1/secrets.
The naming rule
A name must match^[A-Za-z_][A-Za-z0-9_]*$ — letters, digits and underscores, never starting with a digit. That is exactly the identifier shape the {{secrets.NAME}} token parses, so a name that cannot be referenced cannot be stored. A dash, a space or a dot is refused.
Names are case-sensitive and unique in the workspace. SCREAMING_SNAKE_CASE is the convention everywhere in these docs; nothing enforces it.
A value is 1–16,384 characters. An empty value is refused.
Write-only
A stored secret is never returned by any endpoint, to anyone, including the ADMIN who stored it. A read gives you the name and a masked tail:value_hint is the last four characters and nothing more — enough to tell two keys apart when you are rotating one, never enough to reconstruct either. Any member can read this list; only an ADMIN can write it. There is no export, no reveal button and no support path that prints a value back to you. If you have lost the original, store a new one.
Where a secret resolves, and where it does not
Writing
{{secrets.NAME}} in a prompt or greeting fails validation:
Inside a code operation
A code operation has no template syntax, so a secret arrives as a property on the input object instead:
{{secrets.NAME}} templates and for input.secrets.NAME property access, and the workspace’s other secrets are never sent to the sandbox.
A single token keeps its type
A string that is exactly one token resolves to the value itself, with its type intact. A token inside a larger string is substituted as text:token is the stored value as-is, unquoted and unmodified.
A missing name fails loudly
That is enforced twice:- At publish. A tool referencing a secret the workspace does not have is a publish error, so the mistake is caught before anything is live. The same check runs on an inline MCP server’s URL and headers.
- At runtime. Validation cannot catch a secret that is deleted after the tool was published. That one shows up as a failed tool call mid-conversation, with
secret 'NAME' does not exist. Deleting a secret is the one action here that breaks something already live, so check what references it first.
Rotating a value
Store the same name again.POST /v1/secrets is an upsert: the value is replaced, the original creator attribution is kept, and nothing that references the name has to change. Tools pick the new value up on their next run — there is no republish and no cache to clear.
There is no versioning and no undo. The previous value is gone the moment the new one lands.
Deleting is DELETE /v1/secrets/{secret_id}, ADMIN only. Anything still referencing the name fails at runtime from that point on.
Plaintext is promoted automatically
Integrations and carrier accounts accept a credential field in either form:- An existing reference,
{{secrets.RESEND_API_KEY}}. The name has to exist, or the request is refused withsecret 'RESEND_API_KEY' does not exist. - Raw plaintext. It is written into the secrets store under a sensible default name —
RESEND_API_KEYfor a Resend key, say — and only the reference is stored on the integration row.
RESEND_API_KEY_a91c) rather than overwriting the secret that is there. An existing credential another tool depends on is never clobbered by someone connecting an integration.
Either way, plaintext is never stored on the integration or carrier row. The row holds a reference; the value lives in one place, encrypted, with one way to rotate it.
Why a credential must never be a variable
The other template roots are visible to the model and can be read aloud.{{vars.name}} is substituted into the system prompt the model reads. {{userdata.field}} is session state the model reads, tools write, and the agent may repeat back to a caller. A model that can see a value can say it, put it in a summary, or pass it to a tool you did not intend.
A secret is different in kind: it is resolved by the runtime, inside the operation, on the way out to your API. The model never sees it. It cannot be repeated, cannot be leaked into a transcript, and does not appear in a call’s stored request payloads.
So: an API key, a bearer token, a signing secret or a password is a secret. A customer’s account number, a tenant id, a base URL for the environment this agent serves — those are variables. If a value would be damaging in a transcript, it is a secret.
Templating and data
The six roots a tool operation can read, and what each is for.
Provider keys
A different store, for a different thing: the model credentials Talqing runs your agent on.