RFC-137-B: Forge App Suspension Behaviour

RFCs are a way for Atlassian to share what we’re working on with our valued developer community.

It’s a document for building shared understanding of a topic. It expresses a technical solution, but can also communicate how it should be built or even document standards. The most important aspect of an RFC is that a written specification facilitates feedback and drives consensus. It is not a tool for approving or committing to ideas, but more so a collaborative practice to shape an idea and to find serious flaws early.

Please respect our community guidelines: keep it welcoming and safe by commenting on the idea not the people (especially the author); keep it tidy by keeping on topic; empower the community by keeping comments constructive. Thanks!

Project Summary

This RFC defines how Forge app installations behave when they enter a suspended state. We want to give you - as a Forge developer - a clear, predictable contract: what happens to your app’s functions, UI extensions, product events, and storage access during suspension, and what you should do to handle the transition cleanly when your app comes back online.

Publish: 05 June 2026

Discuss: 19 June 2026

Resolve: 03 July 2026

Problem

Your Forge app can enter a suspended state for a number of reasons - for example, when a customer’s Marketplace subscription lapses, when data is being migrated to a new region, or when Atlassian needs to take an app offline for operational reasons.

Today, what actually happens to your app during suspension is not consistent:

  • Your app may continue to appear available in some places while silently failing in others (e.g. invocations blocked). This creates a confusing experience for both, you and your customers.
  • There is no documented, reliable contract for what you can expect during suspension - whether your functions will be called, whether your UI extensions will render, whether product events will be delivered, or whether your storage will be accessible. This also means that when a customer stopped paying for an app, vendors would continue to pay for Forge storage, compute and logs for the time of suspension (60 days).
  • Future app types may not be tied to a single host app (like Jira or Confluence) anymore. This means suspension behaviour that was previously inherited from the host app no longer applies, and these app shapes need their own clearly defined suspension semantics.

The result is that it’s hard to build apps that handle suspension gracefully, recover cleanly, or communicate the right experience to your customers when an app goes offline.

It is also worth noting that when an Atlassian app subscription expires, the apps go offline for 60 days and then are soft-deleted. Forge apps stay online for 60 days and then are soft-deleted. This inconsistent behaviour is confusing for customers.

We want to fix this by introducing a simple, consistent suspension contract across all Forge app types.

Proposed Solution

When a Forge app installation enters a suspended state, it will be fully offline. Your app will not run, render, or receive events during suspension. Here’s exactly what that means:

  • Functions and triggers will not be invoked. This includes scheduled triggers, web triggers, and event-triggered functions. No invocations will occur while the installation is suspended.
  • UI extensions will be hidden. Any modules your app renders in the Atlassian product UI - panels, dialogs, issue glances, macros, etc. - will be hidden or shown as unavailable while the app is suspended.
  • Product events will be dropped (with the exception of Data Residency (see below).
  • Forge Storage will not be accessible, incl Forge remote (ie Forge Storage API calls). Your app will not be able to read from or write to Forge Storage while suspended.
  • Your app remains visible to Admins. The app will still appear in the Connected Apps section of Atlassian Administration, marked as Suspended. Admins can either resubscribe or uninstall the app at any time during the suspension period.

Event Handling: Data Residency Migrations vs. Everything Else

The only case where suspension behaviour differs is for product event delivery during a data residency migration:

Suspension reason What happens to product events
Data residency migration Events are queued while the app is offline and replayed once the migration is complete. Migrations are typically short-lived (up to ~24 hours), making buffering safe and practical. You don’t need to do anything - events will be delivered automatically on unsuspend.
All other suspension reasons (subscription lapse, key revocation, host product suspension, operational suspension, infrastructure maintenance) Events are dropped. They are not buffered and will not be replayed when your app comes back online. You should implement a reconciliation pattern to re-sync your app’s state on unsuspend (see guidance below).

We made this distinction because suspensions due to non-payment can last up to 60 days. Buffering all Atlassian app events for that duration at the volumes involved is not feasible and would potentially overwhelm your app with a large burst of buffered events if unsuspended. For short, bounded suspension windows like data residency migrations, buffering is safe and we will handle it automatically for you.

Suspension Scenarios and What to Expect

Here is a summary of how suspension works across the different scenarios you might encounter:

Scenario What triggers it Functions & triggers UI extensions Product events How it resolves
Data residency migration An admin moves data to a new region Not invoked Hidden / Unavailable :white_check_mark: Queued and replayed after migration Automatic - your app resumes once the migration completes
Customer-managed key (CMK) revocation or re-encryption A customer revokes or rotates their encryption keys Not invoked Hidden / Unavailable Dropped Automatic - your app resumes once the key operation is complete
Marketplace app subscription lapsed A customer’s paid subscription for your app expires or is cancelled Not invoked Hidden / Unavailable Dropped The customer re-subscribes via their Billing Console. If not re-subscribed within the suspension window (up to 60 days), the installation moves to a deactivated state and is deleted after a further 30 days.
Host Atlassian app subscription lapsed The Atlassian app your app is installed in (e.g. Jira, Confluence) is suspended due to non-payment Not invoked Hidden / Unavailable Not applicable - the host app does not emit events when suspended Automatic - your app resumes when the host product subscription is reinstated
Operational suspension by Atlassian Atlassian suspends your app installation due to a policy violation, abuse, or an operational issue Not invoked Hidden / Unavailable Dropped You will be notified in advance and your app will be re-activated once the issue is resolved.
Atlassian infrastructure maintenance Atlassian performs a planned infrastructure or storage migration Not invoked Hidden / Unavailable Dropped Automatic - your app resumes once maintenance is complete. Atlassian will provide advance notice where possible.

What Is Changing: Removal of the “Unlicensed / Free” Mode

Some Forge apps have historically implemented a developer-defined “unlicensed” or degraded mode - keeping the app partially functional after a subscription lapsed, for example by showing a re-subscribe prompt while still serving basic functionality.

Under this new contract, this approach will no longer work. When an installation is suspended, it is fully offline - there is no platform support for a partially functional or degraded mode during the suspension window, regardless of what your app’s code does.

The right time to re-engage customers is before suspension occurs. We recommend using the licenseDetails object in the Forge license API - which exposes the trial end date and current subscription status - to surface warnings to Admins ahead of expiry. This is a better and more reliable re-engagement experience than a degraded post-expiry mode.

Guidance for Developers

Reconciling state after your app comes back online

Because Atlassian app events are dropped during suspension (for everything except data residency migrations), your app may have missed events that occurred while it was offline. We recommend building a reconciliation pattern that runs when your app transitions from suspended back to active:

  • Provide a mechanism to gracefully resync your app’s state if needed. Use the Atlassian app REST APIs to fetch the current state of relevant data (e.g. Jira issues, Confluence pages, user data) rather than relying on any event replay.
  • Make your synchronisation logic idempotent - running it multiple times should not produce duplicate side effects or data inconsistencies.
  • Avoid assuming that no changes occurred while your app was offline. Treat your local state as stale on unsuspend and verify it against the Atlassian app.

We are also considering introducing suspended/ unsuspended lifecycle events that would notify your app explicitly when it gets suspended and transitions back to active, making it straightforward to trigger your reconciliation logic. We’d love to hear whether this would be useful to you.

Scheduled triggers

Scheduled triggers will not fire while your app is suspended. Missed scheduled executions are not queued - they will not run when your app comes back online. Design your scheduled trigger logic to be resilient to gaps: do not assume every scheduled execution has occurred when your app resumes.

Notifying your customers before suspension

For paid apps, the best way to prevent unexpected suspension for your customers is to surface clear warnings before their subscription expires. The licenseDetails object in the Forge license API gives you access to the trial end date and subscription state - use this to show timely, actionable prompts to Admins before the subscription lapses.

Multiple app compatible Forge apps

If you’re building a Forge app that is compatible with multiple Atlassian apps, your installation is not tied to a single host product like Jira or Confluence. This means your app’s suspension state is managed independently - for optional links, your app may remain online while the linked Atlassian app is offline and suspended .

Asks

We are keen to hear from you:

  • Does this suspension contract work for your use cases? Are there scenarios where being fully offline during suspension would cause significant problems for your app or your customers?
  • Would a suspended/ unsuspended lifecycle event - explicitly notifying your app when it comes back online - be useful for triggering reconciliation? How would you use it?
  • Does removing the “unlicensed/free” degraded mode affect you? What patterns do you currently rely on that this change would break?
  • If your design needs a reconciliation pattern, can this be achieved gradually within current API limits and costs?

Hi @JuliaDaehne ,
As a clarification question: What will happen to Forge macros in Confluence? How will they render?

What does this mean for the isActive Boolean in the license object returned from view.getContext() (from @forge/bridge)?

Our custom UI currently uses that to display a message when the license is not valid.

Is the expectation that we should remove all license checks and rely on the platform to display an appropriate message to the end user if the license is inactive?

What about in resolver functions? Do we need to do a license check against the provided context still, or are we to assume that a resolver function will never be invoked for an inactive license?

Not receiving “useless” events that we’d discard after checking the license is a welcome change and – in theory – means that we can skip license checks altogether.

That being said, not having any data about an expired tenant is a missed opportunity for potential marketing activities to re-engage churning customers. While the techie in me is happy that my life will be easier, I see some downsides for the business as a whole.

Another area in which the user experience will be degraded is the workflow editor: apps can provide workflow rules which – if the app is not invoked to render the config – will simply show up as “Unknown rules”. As a customer, not being to see what was going on in the workflow is not good: even if I intentionally let one subscription lapse because I’m consolidating the amount of apps I use, I still want to know what I need to replace.
Of course the same applies for apps whose config is stored/displayed in standalone pages, but this specific case affects native Jira pages.

There are pros and cons, I’m not sure which ones outweigh which.

I am not convinced with the objectives behind this RFC. Maybe it makes sense for events and schedulded triggers.

UI extensions will be hidden. Any modules your app renders in the Atlassian product UI - panels, dialogs, issue glances, macros, etc. - will be hidden or shown as unavailable while the app is suspended.

Please don’t do this. We can simply check the license status and display the proper message as needed (status quo). We do not want Atlassian to take this control away from us.

Forge Storage will not be accessible, incl Forge remote (ie Forge Storage API calls).

Again, I don’t see a reason why this is needed?

Will Forge remotes be informed about this? Potentially through lifecycle calls? That way we can also prevent additional processing on the Remote’s side without having to gracefully handle any potential errors.

I’m with @Nar_ChtN on the UI. Please don’t hide it. I care less about control though and more about the UX of this. When you hide the UI, the user has absolutely no way of know what just happened.

I am still good with checking license state and rendering my own messages. Alternatively, if you disable the UI without hiding it, you can put a message on it saying the app is suspended and why it is suspended. This is a much better experience for the user and is more likely to help them solve the problem. Hiding the UI completely is more likely to have them simply do nothing and lose their data in 60 days.

Hello @JuliaDaehne,
Thank you for the detailed description.

Regarding triggers and functions, I view this RFC positively, as it enables better control of resources and avoids unnecessary waste of calls and budget on instances that do not have a valid license.

I agree with what others have already said about “hiding” applications from the UI. This is counterproductive for two reasons:

  1. Users who normally do not have administrator access could be confused when the app disappears, and they can no longer find it, and might think it is a technical issue.

  2. It often happens that license renewals are forgotten. There is a clear distinction between those who use the app and those who administer the product. The person administering the product may not be aware of the app’s usefulness.

If Atlassian’s goal is to completely suspend the app, taking full responsibility for license control, it would be much more appropriate not to hide the app components, but rather to display a suspension message, explaining to users that the app cannot be used because it does not have a valid license and that they should contact the admin to resolve the issue.
In my opinion, as long as the app remains installed, this should be the most correct behavior.

Finally, I would add that in our experience, the user’s final conversion often occurs when the license expires and is blocked by our controls. Hiding it could have a significant impact on sales.

In our case, we would be fine with Atlassian showing a suspended message in place of our app, as that’s pretty much what we do today when context.license.isActive === false, as long as that message clearly guides the user to engage their admin to check/renew the license.

We would not be comfortable with our app simply being hidden, as others have also said.

In an early iteration of our app we used the appIsLicensed display condition in our manifest which did just that: hid our app when unlicensed.

We quickly realised that this was problematic because a user might not even realise our app was supposed to display but for the invalid license, and we were missing an opportunity to convert expired trials into paid subscriptions.

Please do not make this same mistake.

@JuliaDaehne – Thank you!

I agree with the spirit of the RFC: simplifying licensing checks and managing resource costs for unlicensed apps.

That said, I strongly agree with all the feedback about making this easier for customers to troubleshoot. There are real-world challenges to consider:

Challenge 1 - In practice, users experience licensing errors, not admins .

When asked why Atlassian wants to display Connect EOS warnings to end users, an Atlassian responded:

I believe that logic is much more relevant to app licensing. It would be good for end-users to clearly understand that an expected app is missing because there’s a licensing issue.

Example: I see this happen frequently in Enterprise environments when the app transitions from a trial license to a paid license, but there is a delay in acquiring or applying the paid license (for any number of reasons). When end-users see the licensing error, troubleshooting is MUCH faster. (Especially when the admin thinks the license is applied, and users can provide a screenshot showing it isn’t. That happens.)

Challenge 2 - In enterprises, Jira/Confluence admins don’t always have access to app licensing statuses.

Troubleshooting apps can be harder in Enterprise environments, because app-level admins (Jira/Confluence admins) don’t always have access to the Connected Apps page. (That is often reserved for site-level or organization-level admins.) So reserving the error message for the Connected Apps page will make troubleshooting even more of a chore, as neither end-users nor app-level admins will see the licensing error.

Thank you for this RFC. I understand and agree with the need for suspended state handling when it comes to computational items since things like webhooks etc doesn’t have display conditions and can be costly.

However, just like everyone is mentioning - hiding the app will cause more end user frustration and more support costs for us since this the first step will be to put in a support ticket to us and then we’ll need to walk the customer through the troubleshooting steps to identify why the app is hidden.

We dont mind the app being blocked by a message since we don’t have a business contract with the customer at that point so we don’t want their info leaking to us(in fact that would be good) but we need the end user to know what’s going on.

Reminder to Atlassian that you have this core-app feature which has never been used to notify admins about important actionable requirements for Marketplace apps:

Atlassian will let apps zombify and leave users forever stuck on outdated versions, or recklessly and stupidly plaster warning messages on apps 3 months before EOS - meanwhile not one attempt is made via any communication method to directly notify the only users capable of taking action: admins.

Agree with the feedback from everyone else: Don’t hide things, let us handle the messaging.

+1 to the Don’t Hide suspended apps theme. Terrible UX to have something you use every day just disappear.

Enterprise customers especially will not be impressed if an annual renewal is a few days delayed and the app disappears and background tasks/post functions that are a critical part of their workflow suddenly dies.

Perhaps it is time to allow vendors to communicate to the Jira/Confluence admins?

Chris

Hi @JuliaDaehne – A follow-up question:

How will Jira workflow hooks (Conditions, Validators, Post Functions) behave during an app suspension? Will the Jira workflow engine silently skip these and allow a transition to continue, or will users be alerted to the problem?

You asked:

Yes…if workflows silently stop enforcing business rules due to a licensing problem, then this can cause major problems for our customers and a big loss of trust in the platform.

On the other hand, if you’re not paying for my app, I probably don’t want you to run your workflows for free.

I think the desired behavior has to distinguish between 2 customer patterns

  • A paying customer (typically annual) who has a temporary timing issue getting a new quote/payment to Atlassian for the next year. I believe Atlassian does not hard disconnect customers if their annual subscription is 1 day lapsed. I think the same for marketplace app customers. 60 days tolerance seems reasonable to me over breaking the workflow of all users on day 1 because of an accounting hiccup.
  • A trial only customer who has never paid I would be okay with hard suspending (but still visible user facing messaging) as soon as the trial is expired to limit abuse and vendor costs.

100% agree.

I’m only asking @JuliaDaehne what the intention is as to whether the workflows will just quietly skip the suspended workflow hooks, or whether users will immediately be alerted to a problem. There’s no problem (IMO) in suspending the workflow hooks, as long as it isn’t quiet.

Edit: Personally, I would like to see the workflow transitions fail until an admin makes the intentional decision of either renewing the license or re-configuring the workflow. That’s the safest solution from a data-integrity perspective. And I also agree with Chris’s comment about a grace period for app renewals, as opposed to trial conversions. But regardless, silent failures will cause more problems than they help. (IMO)

Macros are expected to be unavailable during suspension - just like they would show when an app gets uninstalled

Thanks a lot for the feedback! It seems that most of you are feeling positive about the change overall but there are concerns about hiding app UI entirely as it can create a poor customer experience. Users may not understand why an app surface has disappeared, and partners may lose the opportunity to give customers useful context or direct them to the right admin or renewal path.

There are a few ways we could approach this:

  1. We could evaluate showing a standard platform-provided suspension message in the affected app surface instead of simply hiding it.

  2. We could also explore allowing vendors to define a suspension message in their app manifest. Where a vendor-provided message is available, Atlassian could show that message in the suspended app surface without invoking app code. Where no vendor message is defined, we would fall back to the standard Atlassian platform message. This would preserve a predictable platform suspension contract while still giving vendors some ability to tailor the customer-facing guidance

  3. Finally we could validate if to keep the app active for a period of time after license becomes inactive which would give partners the time to define their own message