Prerequisites
- A Confluence Cloud site (e.g.,
acme.atlassian.net). Confluence Data Center and Server are not supported - An Atlassian account with access to that site to authorize the connection, and access to the Atlassian developer console to create the app
- ZLH running with a reachable callback URL
Step 1: Create an OAuth 2.0 Integration in the Atlassian Developer Console
- Go to developer.atlassian.com/console/myapps and click Create > OAuth 2.0 integration
- Name it
ZombieLicenseHunter, accept the terms and create it - Under Permissions, find Confluence API, click Add, then Configure, and add these classic scopes:
read:confluence-user— read user accounts and email addressessearch:confluence— list users through the search API
- Under Authorization, next to OAuth 2.0 (3LO) click Add and set the Callback URL to
{YOUR_ZLH_BASE_URL}/integrations/confluence/callback - Under Settings, copy the Client ID and Secret
Atlassian may warn on the consent screen that the app has not been reviewed. That is expected for a private integration.
Step 2: ZLH-Side Values
- The Callback URL must be exactly
{YOUR_ZLH_BASE_URL}/integrations/confluence/callback— protocol, host, port and path - Scopes requested:
read:confluence-user search:confluence offline_access. The first two must be added under Permissions in Step 1;offline_accessis a token-refresh scope that Atlassian grants without configuration. Self-hosted deployments can override the list withCONFLUENCE_SCOPES - The Client ID and Secret are entered per integration in the UI, not in environment variables
Step 3: Connect via the ZLH UI
- In ZLH, navigate to Integrations > Add Integration
- Select Confluence
- Enter the Client ID and Client Secret from Step 1
- Enter your Confluence Site URL as the site host, e.g.
acme.atlassian.net(https://acme.atlassian.netalso works). No path, port or query string - Click Connect — you are redirected to Atlassian to sign in
- On the consent screen choose the site that matches the URL you entered and click Accept
- You are redirected back to ZLH with the integration active and a first sync queued
After consent, ZLH checks that the site you entered is one the authorizing account can reach with Confluence. One integration covers one site; connect each site separately.
What Gets Synced
ZLH lists users through the Confluence search API (type=user), 50 per page. Only Atlassian user accounts are imported; app and customer (portal) accounts are skipped. For each user it captures:
- Atlassian account ID (used as the stable identifier) and email address
- Public name
- Active status — the search API returns active users only, so every imported user is active
- License type, recorded as
confluence
Confluence does not expose login timestamps, so last-login data is not available. Users who hide their email in their Atlassian profile are imported with an account ID only and show as unmatched. A self-hosted deployment configured with an Atlassian organization admin API key (JIRA_ORG_ID and JIRA_ORG_ADMIN_API_KEY) backfills those emails and records whether each account is managed by your organization.
Troubleshooting
| Issue | Fix |
|---|---|
| "invalid site_url: ..." when saving | Enter the site host only, e.g. acme.atlassian.net. Paths, ports and query strings are rejected, and a host that does not resolve is rejected as "failed to resolve host" — check the spelling |
| Atlassian shows an error instead of the consent screen | The Callback URL in the developer console does not match {YOUR_ZLH_BASE_URL}/integrations/confluence/callback exactly, or the scopes in Step 1 were not added under Permissions |
| "Authorization failed: ..." after Atlassian redirects back | The consent was declined. Retry and click Accept |
| "Failed to complete OAuth handshake. Please check your credentials and try again." | Either the Client ID or Secret is wrong, or the site you entered is not one the authorizing account can reach with Confluence. Enter the site exactly as it appears in the consent screen's site list, or authorize with an account that has Confluence access on that site |
Sync fails with confluence: unexpected status 401 |
The refresh token expired or was revoked. Atlassian refresh tokens expire after 90 days without use. Click Reconnect on the integration's card on the ZLH home page |
Sync fails with confluence: unexpected status 403 |
A scope was removed from the app, or the authorizing account lost access to the site. Re-add the scopes in the developer console and click Reconnect |
| Users have no email address | Those users hide their email in their Atlassian profile. They are still imported by account ID; see the enrichment note above |
| Deactivated users are missing | The Confluence search API returns active users only. Deactivated accounts never appear |
| The site moved to a new URL | Edit the integration, change the Site URL, then click Reconnect so syncs move to the new site |