Case study · Jira and Confluence Cloud · Atlassian Forge
Finding the Jira and Confluence seats nobody uses
Atlassian bills for every user with product access, whether they work in it or not. We built License Audit, one app for Jira and one for Confluence, to list the licensed users who did nothing in a chosen period and what their seats cost. Both are read-only and public on the Atlassian Marketplace. Our own products, built and shipped by our team.
The problem
Paying for seats, with no list of who uses them
Atlassian's user export has a "last seen" date, but that only means the person viewed a page for two seconds. Someone who opens one dashboard a month looks as active as someone who works in Jira all day. The Insights charts show counts, not names.
To see who did any real work, an admin has to search one person at a time. That is fine for twenty names and slow for five hundred. Confluence Standard has no per-user activity report; site analytics need Premium or Enterprise.
The admin needs a list they can act on, and they need to trust it. One wrongly listed colleague and nobody believes the report again.
What we built
The per-person check, done for everyone at once
The admin picks a period in the product's admin settings and runs an audit. A background scan checks every licensed user and builds the report: seats per product as Atlassian counts them, each inactive user with how long they have been inactive, and the yearly cost once a seat price is entered. The list can be searched, filtered and downloaded as CSV or Excel. A read-only Rovo agent answers questions about the latest report.
Only actions count
Changing, commenting on, logging work on or creating an issue in Jira; creating or editing pages, comments and files in Confluence. Being assigned an issue does not count, because someone else did that.
Every candidate is verified
A quick sweep clears most active users. Anyone it cannot clear gets a full check of their own before their name reaches the list.
Seat numbers from Atlassian
In Jira the users come from the licence groups and the seat counts Atlassian bills on. In Confluence the count matches Atlassian Administration.
No write permission
Every scope is a read scope, so the platform itself stops the app from changing anything. The server checks that the caller is an administrator on every request.
Engineering
The hard parts, and how we solved them
- Checking every user without timing out. Forge functions have hard time limits, and an audit can mean thousands of searches. The scan runs on a Forge queue in slices. Each stops well before the 60-second ceiling, saves a cursor and queues the next, so the admin can close the page. A scan that stalls for 20 minutes says so and can be restarted safely.
- Cheap first, exhaustive where it matters. A sweep of recently updated issues clears most people in a few calls. Everyone left gets their own query across all issues the app can see: CHANGED BY on all six fields JQL supports, work logged, and issues created or reported. JQL can't search comments by author, so comments come from the sweep, and on very busy sites the report says when the sweep was capped.
- Rate limits and repeated events. When Jira or Confluence asks the app to slow down, the slice saves its work and resumes after the pause the product asks for. Queue events can arrive twice, so each slice carries a sequence number and duplicates are dropped. An account that can't be checked is never listed as inactive on a guess.
- Confluence doesn't say who holds a seat. Access can come through any group, so the app uses the site's own user search, minus apps, guests and deactivated accounts. That matched the Users figure in Atlassian Administration on both test sites. The search answered apps inconsistently, so this one step runs as the signed-in admin. Creating a page only counts if it happened inside the period, so a colleague's edit to an old page doesn't make its author look active.
- Honest about what apps can't see. Neither product shows apps logins or page views. Last-active dates sit behind an organization API key, and calling that API would cost the apps their Runs on Atlassian status. We kept setup at zero, and every report says up front that people who only read will be listed.
- Storing as little as possible. Storage holds account IDs, products, inactivity bands and seat prices. Names and emails are never stored; they are read from Atlassian when the report is shown or exported. UI Kit can't start a download, so a small Custom UI frame builds the CSV and Excel files in the browser.
- Testing on real sites. The first live run found four bugs that unit tests had missed, including a queue setting that stopped every scan. We fixed them and added tests that load every entry point and match every page call to a server handler. A code audit on 13 and 14 October found more, including a Confluence user listing that could stop early on large sites.
Result
Where it is now
License Audit for Confluence went public on the Atlassian Marketplace on 7 October 2026, and License Audit for Jira on 9 October. Before that, Atlassian verified our company's identity and business as a Marketplace partner on 30 September, and both apps went through its app review and Marketplace security questionnaire. Atlassian approved the Jira app's privacy and security answers on 8 October.
Both run Forge version 2.10.0 in production, and both listings show the Runs on Atlassian trust signal: no servers of our own, nothing sent outside Atlassian.