Case study · Jira and Confluence Cloud · Atlassian Forge

Freeing unused seats, with a re-check and an undo for every change

A list of inactive users is half the job. Someone still has to take each person out of the right groups without touching admins or anyone who came back last week. We built Seat Reclaim for Jira and for Confluence to do that after the admin confirms, and to undo any change. Both went public on the Atlassian Marketplace on 26 October 2026. Our own products, built and shipped by our team.

PlatformJira Cloud and Confluence Cloud
Built withAtlassian Forge, UI Kit (React), Forge storage and queues, Rovo
StatusPublic on the Atlassian Marketplace
TimelinePublic within 16 days of the first commit
Seat Reclaim inside Jira's admin settings: inactive users, what reclaiming each would free, and the yearly cost of the inactive seats.

The problem

Removing access by hand, one person at a time

In Atlassian Administration an admin can open a user, go to the Apps tab and remove access to a product. That takes the person out of every group that grants it. It works, one person at a time. Atlassian's own knowledge base says removing inactive users can't be automated from the Jira interface, and a suggestion to deactivate users after a period of no activity has been open since 2019.

By hand, the admin has to know which groups grant which product, skip admins, service accounts and people on leave, and catch anyone who started working again after the list was made. And a wrong removal means putting the person's old groups back exactly as they were.

What we built

Find, review, confirm, and undo if needed

Seat Reclaim uses the same activity check as our License Audit apps. For each inactive user it shows what reclaiming would free and which groups they would leave, or why they can't be reclaimed. The admin selects people, reads a summary and confirms. The app takes each person out of the groups that grant the product, shows the seat count before and after, and writes the change to History, where it can be undone. It never deactivates an account. A Rovo agent answers questions about candidates and past reclaims, and cannot reclaim anyone.

Re-checked just before the change

The list is a snapshot. At reclaim time the app checks each person's activity again and re-reads their groups. Anyone who has worked since the scan is skipped and named in the result.

Rules in code, not in memory

Never anyone whose access comes from an admin group, never the person running the app, never anyone on the protected list. Only groups that actually free a product are left.

Undo for every reclaim

History records whose seat, who reclaimed it, when, and which groups they left. Undo puts the person back into exactly those groups.

Changes run as the admin

Group changes are made as the signed-in site admin, never as the app. Jira and Confluence themselves only let a site admin make them.

Engineering

The hard parts, and how we solved them

  • Proving the mechanism first. We tested on a demo site before building. Adding a user to a product access group used a seat (34,999 left became 34,998), and removing them gave it back at once. So seats can be freed through group membership alone, with no organization API key from the customer.
  • Knowing which groups grant a seat. In Jira, application roles say which groups grant which product. Confluence has no such API, so the app filters the site's groups by the permission each one holds. The group map is read again at reclaim time, so a group added or renamed since the scan is not missed.
  • A record written before the change. Each reclaim is saved to History as pending before any group is touched, then updated when the product answers. A timeout can't leave someone removed with no record. If a call gets no reply, the entry is marked outcome unknown and Undo adds the person back anyway, since re-adding a member does no harm. Undo keeps working after a subscription ends, so a customer can always give access back.
  • Working inside Forge's limits. A resolver call has a 25-second limit, so reclaims run in batches of three. In Confluence, the user endpoint kept reporting access for a moment after it was gone. The app asks the site directory instead, with short retries, and reports a seat as free only once Confluence confirms it. We found that by running full reclaim and undo cycles on a test site.
  • Groups from an identity provider. When a group is synced from Okta, Microsoft Entra ID or another provider, the product refuses the change. The app recognises that refusal and tells the admin to remove the user in the identity provider.
  • The narrowest write permission. The first Jira release used a broad administration scope. In our code audit we checked Atlassian's API specification and moved to write:group:jira, the narrowest scope that covers leaving and rejoining a group. That meant a major version, since customers must approve scope changes. In both apps a test checks that the group scope is the only write scope and that no other module writes, and another checks that the Rovo agent only reads.

Result

Where it is now

Atlassian approved both apps on 26 October 2026, and both went public on the Marketplace that day. The public Jira version is 3.0.0, the one with the narrower write scope, and Atlassian approved its updated privacy and security answers on 28 October. During the review Atlassian asked for a demo video and a security policy page separate from the privacy policy. We made captioned demo videos from real screens and published a security page for our Atlassian apps.

In production: Seat Reclaim for Jira 3.0.0 since 14 October, and Seat Reclaim for Confluence 2.3.0 since 6 November. Both went through Atlassian's Marketplace security questionnaire, and both listings show the Runs on Atlassian trust signal: no servers of our own, nothing sent outside Atlassian.