Forge rolling releases is now in Preview, and therefore fully supported. However, it remains under active development and may be subject to shorter deprecation windows. Preview features are suitable for early adopters in production environments.
To use Rolling Releases, you must adopt Decoupled Permissions in your app and flag it in your manifest.
We release preview features so partners and developers can study, test, and integrate them prior to General Availability (GA). For more details, see Forge EAP, Preview, and GA.
Rolling releases decouple permissions (scopes and egress) from app code versions. This means you can deploy new code without waiting for admin approval of new permissions.
Currently, when permissions change, the app remains on the old version of the code for all existing installations, and admins are often slow to update the apps, causing version fragmentation.
With rolling releases, when you deploy a new version with permission changes:
Rolling releases help you get compatible code changes to existing installations without waiting for admins to approve new permissions. This means security fixes, bug fixes, performance improvements, and permission-independent features can reach customers sooner, while permission-dependent features stay guarded until approval.
Key benefits:
When an installation is upgraded with a code-only upgrade:
This state is called "decoupled" because the code version is ahead of the permission version.
Graceful handling of missing permissions is the developer's responsibility. Once a rollout is initiated, Atlassian will not roll back the upgrade if issues arise. You must thoroughly test your app in decoupled states before rolling out, and ensure it degrades gracefully when permissions are unavailable. See Developer testing and Permissions SDK.
When a code upgrade creates a decoupled state, admins approve new permissions in Admin Hub. The pending approval view shows the permissions already approved for the installation and the new permissions requested by the latest app version.
If an admin does not approve the new permissions, the app continues running with the old permission set. Your app should skip or gracefully degrade features that require permissions the installation does not have yet.
When an admin approves new permissions in the Admin Hub "Connected Apps" page, the permissions will then match the manifest of that version, and are no longer considered "decoupled".
The Rolling Releases Preview supports rolling out code versions to production installations. Permission changes remain governed by admin approval, so apps must continue to handle decoupled states where code is newer than the approved permission set.
Rolling releases are always on for apps that adopt Decoupled Permissions. You do not need to enable a separate Developer Console toggle, but your app must opt in to decoupled behavior by adding permissions.enforcement: app-managed to the manifest, and using the permission SDK.
Update the permissions section in the app manifest to indicate that app is managing missing permissions. This is done by adding enforcement: app-managed config to app permissions.
1 2app: id: ... permissions: enforcement: app-managed scopes: - ... external: - ...
When enforcement is set to app-managed, your app becomes eligible to enter a decoupled state. In this state, if the app attempts an action that requires a permission not yet granted, it will encounter a permission denied error or have its external request blocked.
It is the developer's responsibility to use the Permissions SDK to check for missing permissions at runtime and handle them gracefully. Atlassian will not roll back your app if issues arise - you must ensure your app degrades gracefully when permissions are unavailable.
Use the Permissions SDK to check for missing permissions at runtime.
The updated code may include changes that depend on new permissions but since only the app code was upgraded, some permissions might be missing. To handle such cases, use the permissions SDK to verify if the permission exists and gracefully handle if the needed permission does not exist.
The Permissions SDK is available for both frontend (@forge/react for UIKit, @forge/bridge for Custom UI) and backend (@forge/api) code.
1 2npm install --save @forge/react
Import the usePermissions hook to check permissions:
1 2import { usePermissions } from '@forge/react'; const MyComponent = () => { const { hasPermission, isLoading, missingPermissions, error } = usePermissions({ scopes: ['read:confluence-content', 'write:confluence-content'], external: { fetch: { backend: ['https://api.example.com'], client: ['https://cdn.example.com'] }, images: ['https://images.example.com'], fonts: ['https://fonts.googleapis.com'] } }); if (isLoading) return <Text>Loading...</Text>; if (error) return <Text>Error: {error.message}</Text>; if (!hasPermission) { return <Text>Missing: {JSON.stringify(missingPermissions)}</Text>; } return <Text>All permissions granted!</Text>; };
The hook returns an isLoading boolean, but this should not be true for any significant amount of time.
1 2npm install --save @forge/bridge
Import the checkPermissions function to check permissions:
1 2import { checkPermissions } from '@forge/bridge'; const App = () => { const [hasPermission, setHasPermission] = useState(false); const [missingPermissions, setMissingPermissions] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { checkPermissions({ scopes: ['read:jira-work'], external: { fetch: { backend: ['https://example.com/something'], }, }, }).then(({granted, missing}) => { setHasPermission(granted); setMissingPermissions(missing); setIsLoading(false); }).catch(err => { setError(err); setIsLoading(false); }); }, []); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; if (!hasPermission) { return <div>Missing: {JSON.stringify(missingPermissions)}</div>; } return <div>All permissions granted!</div>; };
Display conditions
You can disable or show an alternative module in the frontend if permissions are missing using display conditions. See Display conditions: Permissions for more information.
Install the latest version of the @forge/api package:
1 2npm install --save @forge/api@latest
1 2import { permissions } from '@forge/api'; const isPermitted = permissions.hasScope('write:confluence-content')
1 2import { permissions } from '@forge/api'; const isPermitted = permissions.canFetchFrom('backend', 'https://api.example.com')
1 2import { permissions } from '@forge/api'; const isPermitted = permissions.canLoadResource('images', 'https://api.example.com/image.png')
1 2import { permissions } from '@forge/api'; const { granted, missing } = permissions.hasPermission({ scopes: ['write:confluence-content'], external: { fetch: { backend: ["https://api.example.com", "https://blah.com", "https://www.google.com"] }, images: ["https://images.example.com", "https://cdn.example.com"], } })
You must extract the granted property from the returned object.
If your app doesn’t have the right permissions, running an Atlassian app event can result in error from the app code. You can use a filter to stop the function from running at the platform level when the required permissions are missing.
Permissions object is added to the event object so that we can filter out Atlassian app events if certain permissions are missing. Update the expression to filter events if certain permissions are missing. For example, to check the missing scopes use the following expression:
1 2modules: trigger: - key: jira-issue-trigger-filtering-by-expression-for-bug-issue-type function: main events: - avi:jira:created:issue filter: expression: "event.permissions.scopes.includes('write:jira-work') && event.issue.fields?.issueType.name == 'Bug'" function: - key: main handler: index.run
External egress is also available in the event.permissions and can be used for filtering. The following expression can be used to filter out Atlassian app events based on external backend fetch permission:
1 2modules: trigger: - key: jira-issue-trigger-filtering-by-expression-for-bug-issue-type function: main events: - avi:jira:created:issue filter: expression: "(event.permissions.external?.fetch?.backend || []).includes('*.example.com')" function: - key: main handler: index.run permissions: enforcement: app-managed external: fetch: backend: - "*.example.com"
For more examples of expressions you can use, see Filter out Atlassian app events.
To try out your own event filtering expressions against a sample payload, use the Expressions playground.
When an admin consents to the new permissions for your Forge app, the upgrade event is triggered. The event payload now includes the app’s updated permissions, helping you track permission changes during major upgrades. For more information, see the Forge app upgrade event documentation.
The upgrade event is not triggered for code-only upgrades (when only your app’s code changes, and there are no changes to the manifest or permissions).
For apps using Forge Containers, use the Get app installations endpoint to fetch installation details and check the installation's current permission state before running code that depends on newly requested scopes or egress permissions.
For apps using Forge Remote, use the permissions object returned by the Installation Details API to check the scopes and egress permissions approved for an installation. Check this object before running code that depends on permissions that may not be approved yet.
For a developer to ensure their app will work when apps upgrade from previous versions, and they have checked the correct permissions in the correct places, they need to enter a decoupled state with various permission combinations from previous major versions.
To test how your app behaves with different permission levels, you can install directly into a decoupled state:
1 2# Install with permission version 2, code version 2 (coupled) forge install --major-version 2 # Upgrade only code to version 4 (decoupled state: permissions v2, code v4) forge install --upgrade code --major-version 4 # Upgrade both code and permissions to version 4 (coupled again) forge install --upgrade --major-version 4
In Developer Console, the Rollouts page lists rollouts for your app across environments. You can filter the list by environment and status. Each rollout shows the target version, rollout status, environment, rollout progress, failure rate where applicable, and available actions.
For step-by-step navigation, see View app rollouts.
To start a rollout, select Start rollout. Rollouts are managed per environment, so confirm you are starting the rollout for the intended development, staging, or production environment.
To inspect an in-progress or completed rollout, select View details. The rollout details page shows the rollout status, percentage of installations receiving the update, installation and error metrics, installation eligibility, ineligible versions, and the rollout timeline.

You can cancel an in-flight rollout from Developer Console. A cancelled rollout stops progressing to additional installations, while installations that already received the target code version remain on that version.
Atlassian cannot roll back a rollout to a buggy version of your app. The easiest way to fix the issue would be to roll forward using a build tag of a last known good version.
You can restart a cancelled rollout when you are ready to continue. Developer Console records rollout activity in the rollout timeline, including queued, started, completed, cancelled, or failed states and their timestamps.
You can roll out a decoupled state when you enable licensing. Auto-upgrading an installation does not create, extend, or change customer entitlements. If an app was unlicensed before the upgrade, it remains unlicensed after the upgrade, and this state is observable through the Forge License API.
Admins can start a trial or subscription independently through the existing billing flows. Until they do, your app should continue to provide non-breaking unlicensed behavior, such as preserving existing free functionality, showing a clear upgrade path for paid features, or gracefully disabling newly paid capabilities.
We recommend a deprecation window where you maintain the free function to enable a smooth transition for your users.
The following features are under development and are not supported as part of the Rolling Releases Preview:
For a hands-on walkthrough of building an app with rolling releases:
Rate this page: