Skip to main content
A refresher is an ordinary workflow whose output becomes a credential’s value. Nothing about it is OAuth-specific — if you can express the renewal as HTTP steps, you can automate it. The credential runs it, stores what it returns encrypted, and hands the result to every workflow that reads $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 reason secrets exists. Seed the first token by hand, then have the refresher return the new one:
Each run’s rotated token is what the next run reads. Rotated secrets are saved before the value, so an interrupted refresh cannot lose the token that mints the next one.
If the provider rotates and the write then fails, the stored token is dead provider-side and every future refresh fails until you re-enter it by hand. Nothing can recover that automatically.

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.

One refresher, many credentials

Everything provider-specific can live in refresh input rather than in the workflow, so a single published refresher can back several credentials — a staging and a production store, two tenants, different scopes. Each credential supplies its own refresh input and its own secrets.