$credential().
Building one
1
Start from an Input step
Its
data_schema is the contract for the credential’s refresh input — the non-secret configuration handed to each run, like a token endpoint or a scope. Declaring the fields means a misconfigured credential fails immediately with a clear message rather than sending a broken request.2
Call the provider
An HTTP step to the token endpoint. Read setup secrets with
$secret():3
End with an Output step
Whatever it produces becomes the credential’s value:
4
Publish it
A credential only runs published versions. Mark a step as the default start node before publishing — a version with no starting step cannot run, and publishing is permanent.
$secret()
$secret('client_id') reads the credential’s setup secrets — the material needed to obtain a token, not the token itself.
It resolves only inside a run of the credential’s own refresher. An ordinary workflow calling $secret() fails, deliberately: that boundary is what stops anyone who can author a workflow from reading another credential’s client secret.
Because of that, the Variables tab only offers a Secrets group while you are editing a workflow that some credential names as its refresher.
Anything not under
secrets in the output is the credential value, readable by every workflow in the organization. Keep long-lived material — client secrets, refresh tokens — in setup secrets, not in the value.The output contract
An empty or null entry under
secrets means keep the stored one, so a provider that didn’t rotate leaves your secret alone. An output containing only secrets is rejected — a refresher has to produce a value too.
Rotating refresh tokens
Providers that hand back a new refresh token each time are the reasonsecrets exists. Seed the first token by hand, then have the refresher return the new one:
Refresh policies
Before it expires needs an expiry to aim at. When the provider doesn’t report one, the fallback interval is used instead, so the credential still renews.
Only one refresh runs at a time per credential. A duplicate trigger is a no-op rather than a second token exchange — which matters for providers that invalidate the previous refresh token on use. Saving a non-manual policy also kicks off one refresh immediately, so pressing refresh straight afterwards often reports that one is already running; that is the outcome you wanted.
Which version runs
The live version if there is one, otherwise the highest published version. Editing and publishing a refresher therefore changes what every credential using it does, on the next refresh.When a refresh fails
The row shows Refresh failed with the reason on the Last refresh cell. A failed refresh keeps the previous value — stale but usable beats no credential at all — and the next scheduled run tries again.
For the run itself, open the refresher’s executions: a refresh appears there like any other run, with its steps and timings. Its own context and output are redacted, because the token it fetched came from the provider and cannot be matched for redaction.

