Monitoring OAuth token expiration in n8n before it breaks a workflow
By Clément Lacaille (Tech-Bharat) · Published 3 September 2026 · updated 22 September 2026 · 7 min read · how we build and test
Complete FlowKit Bundle — €269
12 ready-to-import n8n workflows, setup guide in French, 14-day guarantee
One morning, the HubSpot sync workflow that had been running smoothly for six months fails on every execution. The error message: invalid_grant. Nothing changed on the n8n side — but the OAuth2 credential's refresh token expired or was silently revoked, and nobody knew until leads stopped showing up in the CRM. This is the classic blind spot of any automation wired to an OAuth provider: Google, HubSpot, Slack, Notion, Salesforce... This guide builds an n8n health check that catches a token about to break before it actually breaks a production workflow.
Access token vs refresh token: what actually breaks
The most common confusion is believing an OAuth credential "expires" as a single unit. In reality, two objects coexist with very different lifespans.
The access token is short-lived — often one hour — and is used directly to call the provider's API. n8n renews it on its own on every execution using the refresh token, with no action required from you: that's the normal, invisible operation of the OAuth2 credential in n8n.
The refresh token has a much longer lifespan (months, sometimes indefinite) but can stop working for reasons that have nothing to do with elapsed time: the user revokes access from their Google or HubSpot account security settings, the source account's password changes, a workspace admin removes the application, or — a common special case during development — the OAuth project is still in "Testing" status on the provider's side. It's this second object that, once invalid, produces the invalid_grant error and stops the workflow cold, with no prior warning.
The Google Cloud "Testing" status trap
On Google integrations (Gmail, Sheets, Drive, Calendar…), a trap that regularly catches teams just getting started: as long as the OAuth project in Google Cloud Console stays in Testing status (unpublished, not verified by Google), every refresh token it issues automatically expires after 7 days, regardless of workflow activity. A workflow that ran flawlessly through a week of testing then abruptly stops on day eight, in production, with no n8n configuration having changed. The fix doesn't happen in n8n but on Google's side: publishing the application ("In production" status) moves the refresh token's lifespan to indefinite, subject to the other revocation causes. The initial setup is covered in our configuring Google OAuth2 in n8n guide.
Why an after-the-fact alert isn't enough
The natural instinct is to rely on the Error Workflow to be notified as soon as a node fails. That's necessary, but insufficient for this specific case: a token revoked on a Friday evening won't trigger its first alert until Monday morning, at the first run of the affected Schedule Trigger — and depending on the workflow's frequency, the window between the actual revocation and the first failed execution can represent hours of lost data (unsynced leads, unprocessed emails, unsent invoice reminders). The Error Workflow reacts to the breakage; a dedicated health check precedes it.
Building a daily credential health check
The idea: a separate n8n workflow, independent from business workflows, that tests the validity of every critical OAuth credential each morning via the lightest possible API call — without touching real data.
Schedule Trigger (daily, before business workflows)
│
▼
List of credentials to test (Set node or Supabase table)
│
▼
For each credential: "light" API call using the credential
│
┌────┴────┐
success failure (401 / invalid_grant)
│ │
▼ ▼
nothing Slack alert + log row
Picking a "light" call per provider
Each provider exposes a low-cost route, built exactly for this kind of check, that consumes neither meaningful quota nor business data:
- Google (Gmail, Sheets, Drive…): a call to the
tokeninfoendpoint or a minimal read likeusers.getProfileon Gmail is enough to validate the token without reading a single email. - HubSpot: a
GETon/account-info/v3/detailsconfirms access without querying a single contact. The initial credential setup is covered in our connecting HubSpot to n8n guide. - Slack: the Slack API's
auth.testmethod was built exactly for this — checking whether a token is still valid. - Notion: a
GETon/v1/users/mereturns the user tied to the token without touching a single page.
In n8n, the service's native node (Gmail, HubSpot, Slack…) is usually enough for this test: pick the lightest operation available (often something like "get profile" or "get me"), wired to the credential you want to check. For a provider without a dedicated node, a plain HTTP Request node with generic OAuth2 authentication does the job.
Routing the failure into an actionable alert
An IF or Switch node after each call separates success from failure (HTTP 401, or a message containing invalid_grant / invalid_token). On failure, the message sent to Slack should let someone act immediately, not just observe:
🔴 Invalid OAuth credential: HubSpot (marketing account)
Detected: {{ $now.toFormat('dd/MM/yyyy HH:mm') }}
Action: reconnect the credential in n8n → Credentials → HubSpot marketing
Impacted workflows: hubspot-lead-sync, weekly-crm-digest
That last line — the list of workflows depending on this credential — is what turns a technical alert into actionable information for whoever receives the message on a Sunday evening. Maintain it by hand at first, or reconstruct it by querying the n8n database self-hosted, as described in our guide on auditing credentials by workflow.
Logging to spot patterns
As with any alert, it's better to log every test result (credential, date, status) into a table rather than rely solely on the memory of a Slack channel. After a few weeks, this lets you spot that a given provider revokes more often than others — often a sign of a shared account whose password changes regularly, or an OAuth scope too broad, triggering security-driven revocations on the provider's side. The logging mechanics are identical to those described in our handling errors in n8n guide: one row per failure, with enough context that you don't need to reopen the workflow to understand what happened.
What the research says about token revocation
This difficulty isn't specific to n8n — it's documented in the academic literature on OAuth 2.0. A study by Park, Kim, Park, and Jung, presented at the WISA (Workshop on Information Security Applications) symposium in 2015 and published by Springer, “A Study of OAuth 2.0 Risk Notification and Token Revocation from Resource Server”, points to a structural limitation of the protocol: the OAuth 2.0 specifications don't natively define a mechanism for the resource server to proactively notify a client that a token has been revoked or is at risk — the client (here, n8n) only finds out when it tries to use it, and fails. In other words, the lack of preventive notification isn't a misconfiguration of your instance: it's a characteristic of the protocol itself, which makes an application-level health check — like the one built above — the only real safeguard available to the integrator.
Common mistakes
- Confusing access token and refresh token in the diagnosis. An expired access token fixes itself on the next execution; an invalid refresh token doesn't. Wasting time re-testing as if it were the former when it's the latter delays the actual fix.
- Leaving a Google OAuth project in "Testing" in production. The 7-day time bomb catches nearly every beginner once, rarely twice.
- Only testing when an incident happens. A health check that doesn't run daily is as good as not having one: its whole value is catching the problem before the first business workflow that depends on it does.
- Alerting without saying what to do. A "HubSpot: error" notification with no credential name or list of impacted workflows wastes as much time as it saves.
- Forgetting rarely-used credentials. A monthly or quarterly workflow is exactly the one whose token is most likely to have expired between two runs — it's the one a daily check protects best.
Extending the check: SSL certificates and PagerDuty escalation
The same daily check covers two other silent deadlines.
SSL/TLS certificates
An automatic Let's Encrypt renewal can fail quietly (revoked DNS rights, CA quota reached, stopped cron). Monitoring the real expiry date of the certificate served in production catches these failures before the outage. List every name explicitly, subdomains included (api., app., webhook.): a wildcard shares one expiry date, but a per-subdomain certificate has its own. A 21 to 30-day threshold leaves time to act, backed by a second 7-day threshold at high priority. Store readings in Supabase to see renewals over time.
Escalating to on-call with PagerDuty
For an alert that has to wake someone up, PagerDuty takes over from Slack. The native PagerDuty node acts on existing incidents (Incident, Note, Log Entry, User) with an account API key. To trigger, acknowledge or resolve an alert from your check, use the Events API v2 instead (events.pagerduty.com/v2/enqueue) with the service's routing_key. Incoming incidents arrive through v3 webhooks on a Webhook node: verify the x-pagerduty-signature header (HMAC-SHA256 over the raw body) before processing.
Bottom line
An OAuth token breaking in production is almost never an unavoidable surprise — it's a predictable event that most teams only discover after the fact, for lack of dedicated monitoring. A few-node n8n workflow, run every morning before business workflows, is enough to turn a silent Friday-evening failure into an actionable Monday-morning alert. If your automations rely on several sensitive OAuth integrations (CRM, mailbox, invoicing), this health check deserves the same standing as backups and execution monitoring — not something bolted on after the first incident. The Compliance & Audit Pack (€149) already ships with the append-only Supabase logging logic described above, reusable as-is to trace this health check's results.
FAQ
Frequently asked questions
Why does my n8n workflow suddenly return an invalid_grant error?
This error means the refresh token tied to the OAuth2 credential is no longer valid: it expired, the user revoked access from the provider's settings (Google, HubSpot...), or the source account's password changed. n8n can no longer obtain a new access token, and the node fails on every execution until you manually reconnect the credential.
Does n8n automatically renew expired OAuth tokens?
n8n automatically renews the access token (short-lived, often one hour) using the refresh token on every execution, with no action needed from you. What it cannot renew on its own is the refresh token itself if it expires or gets revoked: that requires reopening the credential and going through the provider's consent screen again.
Why does a Google refresh token expire after 7 days on some projects?
When a Google Cloud OAuth application is in 'Testing' status (unpublished, not verified by Google), every refresh token it issues automatically expires after 7 days, regardless of workflow activity. It's a security limit imposed on unverified applications, not an n8n setting: the fix is to move the OAuth project to 'In production' status in Google Cloud Console.
How do I know which n8n workflow uses which OAuth credential?
n8n doesn't natively provide a credential-to-workflows cross-reference view. Self-hosted with database access, a query on the credentials_entity table cross-referenced with exported workflow nodes can reconstruct this map, as described in our guide on securing n8n credentials.