Documentation
Secrets
Store a credential once, reference it from actions and polled providers, and keep it out of every configuration that reads it back.
Anything RunDis calls on your behalf needs a credential — a bearer token for an API, a key for a webhook, a password for a mailbox. A secret is where that value lives, so the configuration that uses it holds a name instead of the credential.
Two things follow from that, and they are the reason to bother:
- A configuration is safe to read. Anyone who can see a workflow can see how it is wired without seeing what it authenticates with.
- One value, one place. A token used by three actions is rotated once.
Adding a secret
Open Secrets and choose Add Secret. A secret has a name, the value, and an optional description so the list means something later.
The name is how you refer to it, so name it after what it opens rather than where
it is used — STRIPE_API_KEY rather than BILLING_STEP_TOKEN. Names are unique
within your organisation; two organisations can both have a STRIPE_API_KEY and
they are different secrets.
The value cannot be read back. Not through the interface, not through the API, not by you. The list shows names and descriptions only. If you need to know a value again, get it from wherever it came from originally.
You can replace a value at any time with Replace value. Anything referencing the secret picks up the new value on its next run — there is nothing to redeploy.
Referencing a secret
Write ${secret.NAME} wherever the credential would have gone:
${secret.STRIPE_API_KEY}It can sit inside a longer string, so an action URL that carries a key works too:
https://api.example.com/v1/charges?key=${secret.STRIPE_API_KEY}References resolve at the moment of the request, and only in memory. The stored configuration keeps the reference; the value never gets written back into it.
Where references work
| Place | Where a reference is substituted |
|---|---|
| Any action, all five types | Every text value in the configuration — auth_token, a headers value, a URL, a body |
Provider — outgoing_request |
The Auth Token field, and any headers value |
Provider — run_script |
The Command, resolved when the script runs |
Provider — incoming_webhook in a signature mode |
The Signing secret, resolved on each post the signature is checked on; the whole value must be one reference |
Actions are the broad case: a reference is substituted anywhere text appears in the configuration, so it is not limited to the fields named like credentials.
Polled providers are narrower on purpose — the token and the headers only. The endpoint URL is not substituted, so a key that has to sit in a provider's URL still has to be written there directly. A script provider's command is text a shell runs, so a reference anywhere in it works, the way it does in an action. An inbound webhook's signing secret is a key, not text: the value is exactly one reference, and the stored value is what GitHub, Stripe or Slack signs with.
Triggers and steps have no credentials of their own; a step authenticates through the action it runs.
When a reference cannot be resolved
A reference to a secret that does not exist is an error, and the run fails
with the name that was missing. It is deliberately not a passthrough: sending the
literal text ${secret.STRIPE_API_KEY} as a bearer token would look like a
working action and fail somewhere you cannot see.
So deleting a secret breaks whatever still references it, on purpose and visibly. The confirmation prompt says as much.
Credentials already written into a configuration
A configuration can hold a credential directly — older ones often do, and nothing stops you typing one in today.
Those values are redacted when read. The interface and the API return
__redacted__ in their place, so a credential someone pasted in last year is no
longer readable by everyone who can see the action. Saving the form back leaves
the stored value alone, so editing a URL does not destroy the token beside it.
A reference is not redacted, because ${secret.STRIPE_API_KEY} is a name, not
a credential. That asymmetry is the point: a configuration built on references
stays completely readable.
Redaction hides those values but does not move them. To move them into the
store, replace the field with a ${secret.NAME} reference and add the secret.
What secrets are not
Worth stating plainly, so nothing here is read as more than it is.
- Values are encrypted before they are stored, so a copy of the database does not hand over your credentials. The key is held by the RunDis service, not by a hardware module, and not in a way that makes the values unreadable to the service itself — it has to decrypt them to use them.
- There is no rotation schedule, no versioning, and no expiry. A value can be replaced. That is the whole lifecycle.
- There is no audit trail of secret access. Runs record which action ran, not which secret it read.
- Only admins can add, replace and delete secrets. Members and viewers can see which secrets exist, never their values. Roles are organisation-wide; there is no per-secret permission.