RFC-95: Forge Dynamic Modules

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

Today, Connect supports the ability to register for dynamic modules or dynamic webhooks and many apps are using this feature today. This project seeks to explore how to make dynamic modules available in Forge, allowing Connect apps to migrate to Forge without compromising on the existing flexibility and scalability of dynamic modules.

  • Publish: Jun 5, 2025
  • Discuss: Jun 19, 2025
  • Resolve: Jul 3, 2025

Problem

Dynamic Modules

Dynamic modules and triggers provide numerous benefits such as on-demand feature delivery, reduced app size, and enhanced user experience. By loading only the essential modules at runtime, developers can easily introduce new features or tailor the app to various environments without necessitating app updates.

Users enjoy the flexibility of dynamic modules as it allows them to flexibly customize their Atlassian experience. For instance, a task management app may allow users to activate or deactivate modules for features like calendar integrations or notification preferences based on their workflows. Each installation of an app can use different dynamic module configurations making it a popular feature.

Example Jira trigger module with Jira expression filtering:

A user configured for a work item to be auto assigned to the user based on the work item’s priority. This should work for any work item in project “xxx” (dynamically specified by the user). The app dynamically registers a new trigger listening for ‘avi:jira:created:issue’ and ‘avi:jira:updated:issue’ with expression based filter to receive events only for “xxx” project. As a result, the app will populate the assignee field with the user’s name based on selected priority.

Proposed Solution

Dynamic modules are modules that are added to a specific installation during an app’s runtime. Instead of defining all modules in the manifest file, these modules would be added during app execution. As is the case for static modules today, a resolver for dynamic modules has to be defined in the code for the app to know where the module can be found and loaded at runtime.

To start with, we are proposing a limit of 100 dynamic modules per tenancy.

New APIs

We are proposing the following new APIs for dynamic modules

  1. get a list of previously set dynamic modules
  2. register for module types of interest
  3. update registered modules
  4. delete registered modules

Once the app has registered for one or more dynamic modules, they will be made available to that app installation on demand.

Apps will make requests on behalf of a user using GraphQL asUser(). asApp is not supported, meaning dynamic modules cannot be added by scheduled jobs.

Admin experience

The customer experience is driven by the developer of the app. Customers’ app configuration preferences will determine when a dynamic module is triggered and executed.

We are proposing the configuration of dynamic modules to require the Product Admin role. This will reduce the amount of dynamic modules set. Once dynamic modules are defined, end users can use them on their instance.

Delivery

We are planning to deliver dynamic modules in an iterative manner. This means we will make some dynamic modules available earlier than others. We will likely start with trigger modules for Jira.

As we progress, we will gradually expand our offerings to include additional module types and covering Confluence as well.

Asks

While we would appreciate any reactions you have to this RFC, we’re especially interested in hearing more about:

  1. Does this implementation make sense for your app(s)?
  2. Will this unlock new use cases for you on Forge which were previously inaccessible?
  3. Is there anything you believe we are missing, or which should be reconsidered in the above proposal?
  4. What filtering options would you expect to become available to you for dynamic modules?
6 Likes

Hey Julia,

thanks for sharing this. At least for JMWE’s use cases, this works and is a viable approach to replace webhooks.

I only have minor comments:

  • Not supporting asApp might be somewhat limiting: when we drop Connect and move entirely to Forge we will have to programmatically replace our webhooks with dynamic modules, if we can’t do it asApp, it will be a manual step that will cause some downtime until an admin approves it
  • We have some plans for features that would allow Project admins to create webhooks that only apply to their own project. I totally get why you want “Product Admin” permissions to create dynamic modules, but not having asApp means that we can’t have a custom permission model that allows other users to create dynamic modules under limited circumstances (e.g. only for the project in which I’m an admin)
  • It would be very useful to have an endpoint that checks whether a Jira expression is valid for event filtering, I know that we can get the estimated complexity, but we might have use cases in which the customer writes a Jira expression and it would be super helpful to be able to provide feedback about its validity (e.g. no dynamic data loading)

None of these is a blocker, but if the required effort is manageable, they would provide a better experience for the end users.

Thanks!

5 Likes

Thanks for the RFC @JuliaDaehne! I share Paolo’s concerns above:

  • Not supporting asApp will make it hard for us to migrate from Connect to Forge dynamic modules, given that there is probably no automated migration of dynamic modules.
  • For some use cases, we currently allow any users to add dynamic modules to the Jira work item action menu. Limiting it to Product Admins only seems a bit artificial, since it highly depends on the use case where dynamic modules are being used. Shouldn’t the app take care of permissions itself (see also Shared responsibility model)?
  • Will it be possible to use offline user impersonation to setup dynamic modules in Forge using asUser()? If that’s the case, we would have a workaround for the two concerns above. If not, we need to find another solution.
  • Is the limit of 100 dynamic modules per tenant shared across all apps or would it be 100 dynamic modules per app and tenant?

In any case, making changes like the suggested asUser() limitation to dynamic modules will probably affect many app vendors using Connect dynamic modules today that might not read this RFC. I’d be careful making too many changes for the Forge equivalent, since we’re already struggling with a lot of other changes, differences and blockers when moving Connect apps to Forge. There would need to be a very good reason for those additional limitations. The reason given to “reduce the amount of dynamic modules set” would already be taken care of by setting the limit of dynamic modules per tenant to 100. I’m not sure if the limitation for Product Admins would lead to significantly less dynamic modules being used, but would definitely lead to more friction getting apps setup by customers and app developers to design a smooth user experience.

Again, thanks for sharing the RFC and taking our feedback into account :folded_hands:

3 Likes

I echo the previous concerns about asApp() being a necessity.

Also, is there any possibility of designing the solution in a more generic sense? Reading between the lines, I infer that this will be delivered for a very limited set of module types (“we will make some dynamic modules available earlier than others”). As a vendor, I would hope that almost all module types would be supported (excluding those that do not make sense), such as if vendors were able to upload a manifest.yaml snippet via API call.

If this is not possible, does Atlassian have a preliminary list of what final module types it plans to support across the various host apps?

2 Likes

We would use asApp when available but it’s not our most immediate need, so from our point of view we would not want a requirement for asApp to delay dynamic modules as you have specified it.

1 Like

Hi @scott.dudley planning with a limited set of modules does not mean that we will stop there. It is just a matter of prioritising and delivering iteratively over time. As we start with Jira our list (no particular order) will for instance cover custom field, entity property, issue context and various Forge equivalents of web items and web panels.
Is there any particular use case you are keen to see addressed?

Hi @BenRomberg the limit of 100 dynamic modules is intended to be per tenant shared across all apps.

On your point about the Product Admin restriction:
I understand what you and @PaoloCampanelli are saying about adding friction. One of our reasonings was that if any user can configure dynamic modules there isn’t a way for a customer to track such activity in the audit logs. App configuration is generally an admin action that is tracked in logs. Should there be an inquiry on who added a dynamic module -maybe in the context of the max module limit being reached - an admin wouldn’t be able to investigate the “who did what and when”.
We could evaluate starting without the admin restriction and see what the customer feedback will look like but introducing a restriction might be harder.

The offline user impersonation has not been evaluated as a dynamic modules workaround solution on our end.

@JuliaDaehne, shared across all apps? So, it might happen that my app cannot register a new dynamic module, because other apps in the same tenant already consumed all available 100 slots? That feels weird :confused:

7 Likes

I second Andreas’s point: 100 dynamic modules across apps is – in my opinion – not an acceptable compromise.
On Connect, each app gets up to 100 webhooks and 100 dynamic modules, moving to 100 per app is already a downgrade, but getting a fraction of 100 that you don’t even know until you try (and fail) to register a new module would create terrible UX.

This would encourage dark patterns such as preemptively registering as many dynamic modules as possible to “reserve” them for your app, which is clearly not something anybody would want.

8 Likes

I am just concerned that the solution will designed in such a way that support for each additional dynamic module requires a lot of work, rather than it being a one-size-fits-all solution that supports all or almost all dynamic modules out of the box with little incremental work per module.

In this case, I am worried that the team will run out of steam and eventually stop, or at least find that those competing priorities redirect attention elsewhere before finishing the job. For example, I have been waiting four years for Forge to support some Connect webItem equivalents (which are admittedly at the long end of the tail), and I fear that the dynamic module project will be an equally arduous process. Forge modules seem much more heavyweight than their Connect equivalents, so I infer that I should probably not make any critical business plans to use dynamic modules except for what is announced in the initial launch.

As far as the limit of 100 dynamic modules per tenant, this is a problem. Apps need a guaranteed way to be able to add dynamic modules for core functionality, and having failures created by other apps is not workable, as others have said.

For the lack of asApp() support, dynamic modules are not always added by users, and they are not even necessarily related to a specific user. (Is it true that end users are actually clamoring for user-level traceability of this at an Atlassian level?) The module is within our app and we can already add non-dynamic modules on a whim through version updates without module-level audit trails or specific user consent.

If you really need audits, how about letting the app optionally declare a specific user account when registering the module? You could pipe that through to the audit logs but also permit app-level registrations, and instruct vendors that the user information must be included if the module is related to a user. This provides traceability, and if no user-level information is provided (that was otherwise supposed to be there), this becomes a vendor problem and not an Atlassian problem.

2 Likes

Couldn’t you also show regular users or app users in the audit logs? I just checked our internal Jira’s audit log and there’s all kinds of non-admin Jira users, App users and even JSM customer accounts showing up. Or are we talking about another audit log where only Jira admins can be shown?

Regarding “App configuration is generally an admin action” - I would also say that it depends on the app how configuration takes place. In our apps, we try to give as much power as possible to regular Jira users (without having it get out of control), so that Jira admins are not the bottleneck for app customization. We have received great feedback from customers for this approach and it’s also something setting us apart from other apps in the same space that we wouldn’t want to give up.

In any case, thanks for actively discussing our concerns and taking our feedback into account :folded_hands:

Hi, Julia!

Thank you for bringing it into the discussion!

I represent User Macro for the Confluence Cloud app,
Registering dynamic modules (Confluence macros) is our main feature. An appropriate endpoint plays the key role for the app.
The endpoint is labeled as experimental, which brings risks to us. The uncertain future of the endpoint on Forge made me nervous, to be honest. RFC-95 is a relief: it gives hope for the future of the app and even better flow with improved endpoints.

As we are forced for Forge migration only “Adopt Forge from Connect” will work so far.

I have two concerns:

  1. Backward compatibility
  2. Limitation by the number of modules

Backward Compatibility. How will the migration work from already registered modules by the app?
Will the data structure stay the same?
If migration is needed, it would be very handy to have instructions

Limitation. My power customers use 30-50 macros, from the 100 limitation.
We internally use all of 100, but it is for testing/development.
100 is not unlimited, but quite enough for everybody.
Even 50 would be enough, but this limitation should be clear and straightforward.
Dividing 100 modules between all Applications and all 3rd parties it’s not a clear approach.

If this is the way you decide to go, Admins should have a management page, and vendors should have an endpoint to check the current status programmatically.
So I can imagine a (kind of) user-friendly approach:

  • Admin is going to create a new macro
  • Our app checks the availability of free slots
  • If there is no such: show a warning message with a link to the admin page
  • Admin, free up space for the macro
  • Admin comes back and creates a macro
    Weird, but still better than an error message after failed module registration.

Speaking of direct questions.

3. “Is there anything you believe we are missing, or which should be reconsidered in the above proposal?”
Backward compatibility and migration.

4. “What filtering options would you expect to become available to you for dynamic modules?”
Update/delete by key(or id)/create/get (all and one). The current API for Confluence does not have an update operation (only delete and create new with changes), and the documentation does not represent reality.
If there will be a tenant-level limitation, check for available slots.
With such a restricted number of modules, advanced filtering is not needed. Maybe only by type. So we can focus just on our Dynamic/Static Macros.

Additional things that are a bit out of the topic, but nice to have:

  • More flexible Macro Browser. At the moment, it’s cached on the client side. After each change hard page reload is needed.
  • Modules with visibility: only specific users/roles can add a macro, or only in specific spaces can it be used.
1 Like

I think sadly it’s already been decided that Forge apps will involve a huge amount of in-code complexity to manage dynamic admin controls.

  • Dynamic modules (RFC)
  • Configurable egress (RFC)
  • App versioning (TBD)

Essentially admins will be given the controls to switch on/off any granular permissions, scopes, egress, APIs or modules at any moment and apps will need to handle that.

What will likely be needed is some sort of opinionated framework or Forge package to avoid an infinite fractal of if-else statements throughout a code base.

Hi @BenRomberg the admin.atlassian.com audit logs do not log user activity. If I was to configure a new Jira Deep Clone pre-set this would not show in the customer facing logs.
I also understand what you are saying about “Jira admins not becoming a bottleneck for app customization” though. Thanks for that input

@PaoloCampanelli @AndreasEbert @scott.dudley thanks for the feedback on the proposed dynamic module limit. We will look into this again

1 Like

@JuliaDaehne thanks for clarifying, I thought you were referring to the audit log within Jira “System” settings.
I can also see activity by non-admin users in the admin.atlassian.com audit logs (within “Security”), e.g. “Started chat with Rovo agent”, but understand that there may be other limitations that would prevent you from using the audit log for dynamic modules triggered by regular users or app users.

For the most part this sounds great - we are very interested in this feature as a means to allowing customers to have greater control over what product events are sent to the app.

Is there anything you believe we are missing, or which should be reconsidered in the above proposal?

As others have said, the big problem here is the limit being shared across all installed apps for a single tenant. Apps need to have a predictable number of modules available for them. If one app can essentially break another by taking all the module slots then that will result in chaos.

What filtering options would you expect to become available to you for dynamic modules?

In Connect, webhook modules could specify a JQL expression for filtering, but the same can’t be said for forge. If dynamic modules are more end-user orientated then it makes sense for a more user-friendly syntax like JQL to be possible.

1 Like