
Documentation
Markdown Exporter for ConfluenceBrowse
Documentation
Markdown Exporter for ConfluenceAuthentication
The REST API uses OAuth 2.0 (3LO) — every request must include a valid Bearer token.
Authorization: Bearer YOUR_ACCESS_TOKEN
Access tokens expire after 1 hour. For automated pipelines, use refresh token rotation to obtain new tokens without user interaction.
Required Scopes
Both scopes must be present in your OAuth integration before authorizing:
| Scope | Purpose |
|---|---|
export:markdown:custom |
Grants access to the export endpoints |
read:forge-app:confluence |
Required for Forge API route access |
Include offline_access in the authorization URL to receive a refresh token.
Token Rotation
How to Refresh
curl --request POST \
--url 'https://auth.atlassian.com/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "refresh_token",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"refresh_token": "YOUR_STORED_REFRESH_TOKEN"
}'
The response includes a new access_token and a new refresh_token.
Important — rotating refresh tokens: Atlassian issues a new refresh token on every refresh call. Always save the latest refresh token returned. Reusing an old refresh token invalidates the entire chain and requires re-authorization from scratch.
Refresh Token Expiry
Refresh tokens expire after 90 days of inactivity. Any active pipeline that refreshes at least once every 90 days keeps the token alive indefinitely.
Automation Pattern (Node.js)
import fs from 'fs';
const TOKEN_FILE = './tokens.json';
const CLIENT_ID = process.env.ATLASSIAN_CLIENT_ID;
const CLIENT_SECRET = process.env.ATLASSIAN_CLIENT_SECRET;
function loadTokens() {
return JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8'));
}
function saveTokens(tokens) {
fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2));
}
async function getValidToken() {
const tokens = loadTokens();
const payload = JSON.parse(Buffer.from(tokens.access_token.split('.')[1], 'base64').toString());
const expiresAt = payload.exp * 1000;
if (Date.now() < expiresAt - 5 * 60 * 1000) {
return tokens.access_token;
}
const res = await fetch('https://auth.atlassian.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'refresh_token',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
refresh_token: tokens.refresh_token,
}),
});
const newTokens = await res.json();
if (!newTokens.access_token) throw new Error(`Token refresh failed: ${JSON.stringify(newTokens)}`);
saveTokens({ access_token: newTokens.access_token, refresh_token: newTokens.refresh_token });
return newTokens.access_token;
}
Automation Pattern (Python)
import json, time, base64, os, requests
from pathlib import Path
TOKEN_FILE = Path('tokens.json')
CLIENT_ID = os.environ['ATLASSIAN_CLIENT_ID']
CLIENT_SECRET = os.environ['ATLASSIAN_CLIENT_SECRET']
def load_tokens():
return json.loads(TOKEN_FILE.read_text())
def save_tokens(tokens):
TOKEN_FILE.write_text(json.dumps(tokens, indent=2))
def get_valid_token():
tokens = load_tokens()
payload_b64 = tokens['access_token'].split('.')[1]
payload = json.loads(base64.b64decode(payload_b64 + '=='))
if time.time() < payload['exp'] - 300:
return tokens['access_token']
r = requests.post('https://auth.atlassian.com/oauth/token', json={
'grant_type': 'refresh_token',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'refresh_token': tokens['refresh_token'],
})
r.raise_for_status()
new_tokens = r.json()
save_tokens({'access_token': new_tokens['access_token'], 'refresh_token': new_tokens['refresh_token']})
return new_tokens['access_token']
Security Best Practices
- Store tokens in environment variables or a secrets manager — never in source code
- Never commit
tokens.jsonor any file containing tokens to version control - Use separate OAuth integrations per environment (dev, staging, production)
- Re-authorize immediately if a refresh token is invalidated
Related Documentation
- Getting Started — Initial OAuth setup
- API Reference — Endpoints and request formats
- Troubleshooting — Auth error reference
Guides for Markdown Exporter for Confluence
Stuck on something this page does not cover? Ask the developers.