Confluence OAuth refresh token invalid_grant — What it means & how to fix it
How to diagnose and fix Confluence refresh token invalid_grant errors
Confluence integrations that sync spaces, pages, or knowledge-base content rely on a healthy OAuth token lifecycle. Your token refresh request starts returning invalid_grant. Scheduled syncs stall, writes fail, and users experience a “disconnected Confluence” state until you repair the grant.
This article shows how to identify Confluence refresh-token failures, explains the Atlassian-specific reasons they happen (rotating refresh tokens + concurrency), and outlines practical fixes so they don’t keep recurring. For more background, see API Auth Is Deeper Than It Looks and Why is OAuth still hard in 2025?.
Spot the error
Even if you search for “Confluence OAuth”, the OAuth authorization server is Atlassian. Token refresh happens against auth.atlassian.com, and Confluence API calls go via api.atlassian.com (not {your-site}.atlassian.net). In other words, Confluence shares the same OAuth setup as Jira: same token endpoint, different API routing.
When your backend refreshes tokens:
- Token endpoint:
https://auth.atlassian.com/oauth/token - Often HTTP status:
403 - OAuth error:
invalid_grant
Example response payload (shape):
Atlassian’s docs often phrase this as: “Unknown or invalid refresh token.” Practically, it means the refresh token you’re presenting can’t be exchanged for a fresh token pair. Replaying that same refresh token usually won’t recover the connection.
Why did Confluence/Atlassian reject the refresh token?
For Confluence Cloud OAuth (3LO), invalid_grant is almost always a token lifecycle issue. The root causes are usually not Confluence-specific — they’re Atlassian OAuth-specific.
Most common: you didn’t persist the rotated refresh token
Atlassian uses rotating refresh tokens: every successful refresh returns a replacement refresh token, and invalidates the one you just spent. If your app continues using the previous value, the next refresh attempt fails.
Common triggers include:
- Your code stores the access token but fails to persist the new refresh token
- A retry replays the same refresh token after a timeout
Two refresh attempts overlap (concurrency)
If multiple workers refresh the same Confluence connection concurrently:
- Worker A refreshes and stores the new refresh token
- Worker B refreshes with the now-disabled refresh token
- Result:
invalid_grant(and sometimes state corruption if writes aren’t atomic)
If you want the practical patterns for this, see How to handle concurrency with OAuth token refreshes.
The refresh token hit inactivity expiry
Atlassian rotating refresh tokens have an inactivity expiry window (commonly 90 days by default). If the integration is idle longer than that window, refresh fails and the user must re-authorize.
The user revoked the app grant
If the user revokes access to your 3LO app, refresh tokens stop working for that grant. The fix is to restart the authorization flow.
The user’s Atlassian account password was changed
Atlassian explicitly lists password changes as a reason the refresh-token exchange can return invalid_grant.
Wrong client credentials / wrong 3LO app
Using the wrong client_id/client_secret (wrong environment, a rotated secret not deployed, or simply the wrong 3LO app) can also cause refresh failures.
How to fix it
1) Persist the rotated refresh token (every refresh)
On every successful refresh, replace the stored refresh token with the new refresh token Atlassian returns.
2) Single-flight refresh per connection
Ensure only one refresh is in flight per connection. Everyone else should wait and reuse the updated token set. Persist (access_token, refresh_token, expires_at) together.
3) Verify the Atlassian OAuth endpoints (Confluence uses Atlassian OAuth)
Core endpoints:
- Authorize:
https://auth.atlassian.com/authorize(withaudience=api.atlassian.comandoffline_access) - Token + refresh:
https://auth.atlassian.com/oauth/token
Confluence request routing:
- Get
cloudid:https://api.atlassian.com/oauth/token/accessible-resources - Call Confluence APIs:
https://api.atlassian.com/ex/confluence/{cloudid}/{api}
Note: {api} is the Confluence REST path (for example, Confluence Cloud commonly uses /wiki/rest/api/... routes). If you’re calling {site}.atlassian.net/wiki/rest/... with a 3LO access token, you’re using the wrong base URL for OAuth 2.0 (3LO). If you’re also integrating Jira in the same product, see Jira OAuth refresh token invalid_grant — What it means & how to fix it.
If you’re debugging callback URLs locally, see 3 easy ways to do OAuth redirects on localhost (with HTTPS).
4) If it’s terminal: re-auth (don’t retry forever)
After you’ve ruled out stale refresh tokens, concurrency, and credentials:
- Flag the connection as “needs re-auth”
- Stop scheduled sync jobs for that connection
- Send the user back through the authorization flow
How to prevent Confluence refresh token issues
Use this checklist to reduce “why did my Confluence API integration break?” tickets:
- Store the replacement refresh token every time
With rotating refresh tokens, the previous refresh token becomes invalid after use. - Single-flight refresh + atomic writes
Lock refresh per connection and persist the full token set together, in one update. - Plan for inactivity expiry
If a customer doesn’t use your Confluence integration for long periods, plan for an eventual reconnect. - Clear reconnect UX
A fast “Reconnect Confluence” flow reduces support load and avoids retry storms.
Skip the headache, let Nango refresh for you
Nango's open-source API auth offers:
- 600+ pre-built OAuth flows, including full support for all Confluence APIs
- Automatic OAuth access token refreshing and rotation
- Webhooks when a refresh token is revoked, so you can warn the user instantly
- Built-in error handling for all OAuth edge cases
If you're building a Confluence API integration and you don’t want to maintain Atlassian OAuth edge cases (rotating refresh tokens, cloudid routing, and revocation handling), Nango can run the refresh pipeline for you.
Focus on product features, and let Nango handle the token lifecycle.





