Refresh Tokens vs API Keys: Handling OAuth2 Expiry in Automations
6 min read · Updated Aug 5, 2026

An OAuth2 access token expires, usually in under an hour, by design, so it cannot sit around as a long-lived secret the way an API key does. A refresh token is the separate, longer-lived credential your automation stores and uses to silently get a new access token when the old one expires, without asking the user to log in again. Handling this correctly in an automation pipeline means storing the refresh token securely, catching a 401 from an expired access token, exchanging the refresh token for a new access token automatically, and knowing what to do when the refresh token itself has been revoked or expired.
Key takeaways
- An access token is short-lived by design, often 30 to 60 minutes, limiting the damage window if one leaks.
- A refresh token is longer-lived (days, weeks, or indefinite depending on the provider) and exists specifically to get a new access token without repeating the full user consent flow.
- In n8n and most automation tools, a 401 mid-workflow on a credential that "worked yesterday" is almost always an expired access token, not a broken integration. Refresh automatically and retry once before treating it as a real failure.
- Some providers revoke a refresh token after a fixed period of inactivity, or after the user revokes access manually, which a silent refresh cannot fix. Only re-authentication can.
- Never treat a refresh token like an API key. It should trigger a specific "get a new access token" code path, never be sent directly as if it were a bearer credential on a normal API call.
Why the access token expires and the refresh token does not
The access token is what actually goes on every API request, which makes it the credential most exposed to leaking, through logs, through a debugging session, through a misconfigured error message. Keeping its lifespan short limits how much damage a leaked one can do. The refresh token, by contrast, is only ever sent to the provider’s token endpoint, never to the third-party API you are actually calling, so it has a much smaller exposure surface and can safely live longer. That asymmetry, short-lived and widely used versus long-lived and narrowly used, is the entire design logic behind having two tokens instead of one.
The refresh flow, end to end
When the access token expires, your server exchanges the stored refresh token for a new access token, and often a new refresh token as well, depending on the provider.
curl https://oauth.example.com/token \
-d grant_type=refresh_token \
-d refresh_token={{STORED_REFRESH_TOKEN}} \
-d client_id={{CLIENT_ID}} \
-d client_secret={{CLIENT_SECRET}}Catching expiry in an automation pipeline
Wrap every OAuth2-authenticated call with a check for a 401, refresh once, retry once. Anything beyond one retry after a refresh usually means the refresh token itself is no longer valid, not that the timing was unlucky.
async function callWithAutoRefresh(fn, credentials) {
try {
return await fn(credentials.accessToken);
} catch (err) {
if (err.status !== 401) throw err;
const refreshed = await refreshAccessToken(credentials.refreshToken);
if (!refreshed) {
throw new Error('Refresh token invalid, re-authentication required');
}
await saveCredentials(refreshed);
return fn(refreshed.accessToken);
}
}
The four days of silently missing reports
In September 2023, a workflow I built for a marketing ops team pulled data from a connected Google account every morning at 6 a.m. through n8n. Six weeks in, it started failing silently, not with an error node triggering, but with an empty result set that nobody noticed for four days. The access token had expired overnight, and the custom HTTP Request node calling an endpoint n8n had no native node for was passing the stale token directly with no refresh logic in front of it, so every request came back with a 401 that fell into an unguarded branch treated as "no data today" instead of a real error. Adding the automatic refresh-and-retry pattern above fixed the workflow permanently. Reconstructing four days of missing reports by hand was the least enjoyable part of that week.

Side by side
| Factor | Access token | Refresh token |
|---|---|---|
| Typical lifespan | 30 to 60 minutes | Days to indefinite, provider-dependent |
| Sent to | The third-party API on every request | Only the provider’s own token endpoint |
| Purpose | Proves the current request is authorized | Gets a new access token without user interaction |
| What expiry means | Refresh automatically, usually invisible to the user | Full re-authentication required, user sees the login screen again |
I think the most common automation bug involving OAuth2 is not a security bug at all, it is teams treating "connected" as a permanent state after the initial consent screen, when it is actually a lease that needs active renewal. n8n’s built-in OAuth2 credential type handles refresh automatically for you inside its native nodes, which is exactly why this pain shows up almost exclusively in custom HTTP Request nodes calling an API n8n has no dedicated node for, where nobody wired up the refresh step by hand.
Google’s OAuth2 documentation (accessed 2025) states that access tokens typically expire in one hour, and that a refresh token can itself be revoked after roughly six months of the associated app going unused. That second detail matters for any automation that runs infrequently: a workflow that only fires once a quarter can find its refresh token has quietly expired between runs, with no error until the very next attempt.
“Connecting an account is not a one-time event. It is a lease, and leases expire whether or not you were watching.”
Frequently asked questions
Frequently asked questions
What is the difference between an access token and a refresh token?
An access token is the short-lived credential sent with every API request to prove the current session is authorized. A refresh token is a longer-lived credential used only to obtain a new access token when the old one expires, without asking the user to log in again.
Why does an access token expire so quickly?
Because it is the credential most exposed to leaking, sent on every single API call, potentially logged, cached, or exposed in debugging. A short lifespan limits how much damage a leaked access token can cause before it stops working on its own.
What happens when a refresh token itself expires or gets revoked?
The automated refresh silently fails, and there is no way around it programmatically. The user has to go through the full OAuth2 consent flow again to reconnect the account, which is why a pipeline should surface this clearly rather than retrying forever against a dead refresh token.
How do I handle token refresh automatically in n8n?
n8n’s built-in OAuth2 credential type handles refresh automatically for any of its native nodes. For a custom HTTP Request node calling an API without a dedicated n8n node, you need to wire up the refresh-on-401 logic yourself, since it is not automatic outside the native credential system.
Is a refresh token as sensitive as a password?
It is arguably more sensitive in an automation context, since it can silently mint new access tokens indefinitely until revoked. Store it encrypted, never log it, and never send it anywhere except the provider’s own token endpoint.
Why did my automation that "worked yesterday" suddenly fail with a 401?
Almost always an expired access token, not a broken integration or a revoked permission. Add automatic refresh-and-retry logic in front of the call, and only treat a 401 as a real failure if it persists after a successful token refresh.