Last updated Feb 23, 2024

This section describes a Forge preview feature. Preview features are deemed stable; however, they remain under active development and may be subject to shorter deprecation windows. Preview features are suitable for early adopters in production environments.

We release preview features so partners and developers can study, test, and integrate them prior to General Availability (GA). For more information, see Forge release phases: EAP, Preview, and GA.

Forge remote overview

The Forge platform offers built-in compute and storage capabilities allowing you to build natively on the Atlassian platform. We encourage you to leverage these features when your use case permits.

However, there are certain use cases where you will need to integrate your Forge app with a remote backend and Forge provides functionality to assist with that. Forge allows you to:

If you decide to integrate remote services with Forge you take on additional responsibilities under the Forge Shared responsibility model.

Calling a remote back end from a Forge front end

You can connect your front end (Custom UI and UI Kit 2) to your remote back end via a resolver. To do so, you specify the baseUrl to be used for remote invocations by the resolver in your app's manifest.yml. You can then call resources of that API using the Forge invokeRemote function in your front end, and Forge will automatically route the invocation requests to the correct endpoints.

These requests will include a signed token that enables your remote to securely verify the request came from Atlassian and is intended for your app. You can use any standard JWT library to do this with a public key which we expose through a JWKS endpoint.

Forge Remote invocation

Calling product and Forge storage APIs from a remote back end

If your remote needs to make requests back into product and Forge Storage APIs then you can request access tokens which will be included with the outbound requests out to your remote. These access tokens will be valid for up to 60 minutes.

Remote endpoints invoked by your app's front end can optionally request an app user token, to allow the remote to invoke Atlassian APIs with the current user's permissions. All remote endpoints can optionally request an app system token, to allow the remote to invoke Atlassian APIs with a generic "bot" user's permissions.

Remote delivery of product events, lifecycle events, and scheduled triggers

You can configure product events, lifecycle events and scheduled triggers to be delivered directly to your remote back end. Specify remote endpoints in your app’s manifest and Forge will automatically route events along with a Forge Invocation Token and an optional app access token.

Given the high-volume nature of Forge events, remote invocations will have a timeout of 5 seconds. We recommend that you implement processing logic in an asynchronous fashion (for example, enqueuing events for a worker to process asynchronously). The diagram below illustrates such a solution.

Forge Remote event delivery

Configuring your manifest to use a remote back end

To define a remote endpoint for a Forge module:

  1. Specify the endpoint your app will send remote requests to. For product modules, this is done using the resolver.endpoint property of the module that will be making remote requests. For events specified in a trigger module, this is done using the endpoint property of the trigger module defined for the event. Using endpoint rather than function tells Forge that your app will invoke one or more remote endpoint paths.

  2. Define an endpoint item with a key matching the endpoint name you specified in the previous step.

    1. Set the remote property to the key that uniquely identifies the remote system the endpoint will communicate with.

    2. If invoking this endpoint from an event module, set the route.path to the REST API operation path to be appended to the remote’s baseUrl, to invoke the desired REST API.

    3. If you want Forge to provide system or user tokens, request them in the auth property.

  3. Define a remotes item with a key matching the remote name you specified in the endpoint, setting the baseUrl to the site URL prefix to prepend to the routes specified in your app's route.path or invokeRemote request.

Example manifest.yml

Here's an example of how to define a front-end product module using Forge remote capabilities in the manifest.yml file.

1
2
modules:
  macro:
    - key: forge-remote-app-node
      resource: main
      resolver:
        endpoint: remote-macro-node
      title: forge-remote-app-node
      description: Invokes a Forge Remote NodeJS Server and renders the result.
  endpoint:
    - key: remote-macro-node
      remote: remote-app-node
      auth:
        appUserToken:
          enabled: true
        appSystemToken:
          enabled: true 
resources:
  - key: main
    path: static/hello-remote-world/build
permissions:
  scopes:
    - read:app-system-token
    - read:app-user-token
remotes:
  - key: remote-app-node
    baseUrl: https://forge-remote-refapp-nodejs.services.atlassian.com

Remote contract

This section defines the contract used by remote requests including HTTPS protocol level details, request and response schema.

API architectureREST
ProtocolHTTPS
MethodsGET, POST, PUT, PATCH, DELETE
Timeout - UI modules25s
Timeout - Events5s
Retries - UI modulesNone
Retries - Events4 (details below)
Response headers

Content-Type: application/json

Custom headers: You can also return custom headers in your API response.

Method usage

When using Forge remote to send events to a remote back end, the request is automatically routed to the endpoint configured in the manifest using the POST method.

When using invokeRemote in your Forge app’s front end to make requests to a remote back end, you specify the method and path for the endpoint in the invokeRemote call.

IP address ranges used when invoking remote back ends from Forge

When communicating with remote back ends, Forge uses IP address ranges as indicated in the Outgoing connections section of IP addresses and domains for Atlassian cloud products. This information may be important if your app’s remote back end is behind a firewall or other network equipment that restricts access based on IP address.

Request headers

The following headers are added to requests to your remote.

HeaderRequiredDescription
x-b3-traceidYesThe TraceId is 64 or 128-bit in length and indicates the overall ID of the trace. Every span in a trace shares this ID.
x-b3-spanidYesThe SpanId is 64 or 128-bit in length and indicates the position of the current operation in the trace tree. The value should not be interpreted: it may or may not be derived from the value of the TraceId.
authorizationYesThe Forge Invocation Token (FIT) is passed as a bearer token.
x-forge-oauth-systemNoThe app system token. This is used to call Atlassian APIs from your remote back end. This header is included only if endpoint.auth.appSystemToken is true in the app's manifest.
x-forge-oauth-userNoThe app user token. This is used to call Atlassian APIs from your remote back end on behalf of a user. This header is included only if endpoint.auth.appUserToken is true in the app's manifest.
Custom headersNoWhen using the invokeRemote method from front end modules, you can apply custom headers to the request.
1
2
x-b3-traceid: a523b7549f0b88c9
x-b3-spanid: 2a2436c64727923f
authorization: Bearer ${FIT}
x-forge-oauth-system: ${OAuth-System-Token} 
x-forge-oauth-user: ${OAuth-User-Token}
header1: value ## Set customer headers when invoking your remote from a UI module

Errors

Requests that exceed the timeout or do not return a 2xx HTTP Status Code, will be considered as a failure and will appear in the developer console metrics.

2xx response codeThe remote successfully processed the request.
3xx response codeRedirects are not supported and are treated as an error status code.
401 response codeThe JWT token validation failed.

Event retries

If the remote endpoint returns a non-2xx response or if the invocation times out when delivering an event to a remote endpoint (the timeout period for an event invocation is 5 seconds), redelivery will be attempted as described by the note regarding retryOptions below.

Information on the retry attempt will be provided in the retryContext property sent in the HTTP request body. For example:

1
2
{
  "payload": {
    ...
    "retryContext": {
      "retryData": null,
      "retryCount": 2,
      "retryReason": "INVALID_REMOTE_INVOCATION_ERROR"
    }
    ...
  }
}

See the developer documentation for retrying product events for more information.

retryOptions cannot be set from remote invocations. By default:

  • All remote product event invocations will be retried with a delay of 60 seconds per request.

  • Events will be retried up to a maximum of 4 attempts.

  • retryData will always be null

  • retryReason can be the following:

    • INVALID_REMOTE_INVOCATION_ERROR
      General error. For example; the response from the remote server was non 2XX, the response did not follow the API contract defined in the Forge Remote Preview documentation or there was a network error.

    • TIMEOUT_ERROR
      The request timed out.

    • INVALID_FORGE_INVOCATION_TOKEN
      The invocation token could not be validated and the remote server responds with a 401.

The Forge Invocation Token (FIT)

Requests to your remote back end will include a Forge Invocation Token (FIT) as a bearer token in the authorization header. The FIT includes important information about the invocation context and should be used to verify that the request came from Atlassian and is intended for your app.

The Forge Invocation Token contains a JSON object with the following properties:

PropertyTypeRequiredDescription
appobjectYesInformation about the app and installation context.
app.installationIdstringYes

Identifier for the specific installation of an app. This is the value that any remote storage should be keyed against.

Example: ari:cloud:ecosystem::installation/75969db9-dc7b-4798-9715-bd098ac0d9d1

app.apiBaseUrlurlYes

API base URL where all product API requests should be routed.

Example: https://api.atlassian.com/ex/confluence/4c822e2f-510f-48b9-b8d2-8419d0932949

app.idstringYes

The Forge application ID. This should match the value in your Forge manifest.yml file.

Example: ari:cloud:ecosystem::app/77334c21-3dd0-474f-a53f-28b4eeee5a71

app.versionnumberYes

The Forge application version being invoked.

Example: 1

app.environmentobjectYesInformation about the environment the app is running in.
app.environment.typestringYes

The Forge environment type that this invocation was generated for.

Examples: DEVELOPMENT, STAGING, PRODUCTION

app.environment.idstringYes

The Forge environment id that this invocation was generated for.

Example: ari:cloud:ecosystem::environment/3bb5deab-afcd-4140-9be4-f837b4b14b2c

app.moduleobjectYesInformation about the module that initiated this remote call.
app.module.typestringYes

The module type initiating the remote call. This will be the module type, such as `xen:macro` for front-end invocations. Otherwise, it will be core:endpoint. To determine the type of module that specified the remote resolver, see payload.context.extension.type.

Example: core:endpoint

app.module.keystringYes

The Forge module key for this endpoint.

Example: forge-remote-app-boot

contextobject

The context depends on how the app is using Forge Remote:

  • Remote back end to Custom UI app: See Custom UI resolver. The context includes a variety of information, such as the details of the extension using the remote resolver.
  • Product event handler: See Product event reference. The context includes product-specific information such as the identifier of the product data related to the event, depending on the event that invoked the remote.
  • Life cycle event handler: See Life cycle event reference. The context includes information such as the installer account and app installation ID.
principalstring

The identifier for the user who invoked the app. UI modules only.

1
2
{
  "app": {
    "id": "ari:cloud:ecosystem::app/8db33809-1f32-48bb-8c52-5877dab48107",
    "version": "16",
    "installationId": "ari:cloud:ecosystem::installation/0a3a7799-53ae-4a5b-9e7e-03338980abb5",
    "apiBaseUrl": "https://api.stg.atlassian.com/ex/confluence/d0d52620-3203-4cfa-8db5-f2587155f0dd",
    "environment": {
      "type": "DEVELOPMENT",
      "id": "ari:cloud:ecosystem::environment/8db33809-1f32-48bb-8c52-5877dab48107/aa911f10-c54b-4b93-9e27-dd2947840b9e"
    },
    "module": {
      "type": "xen:macro",
      "key": "forge-remote-app-boot"
    }
  },
  "context": {
    "localId": "4654fa12-4c7c-4792-95a9-6019edb27953",
    "cloudId": "d0d52620-3203-4cfa-8db5-f2587155f0dd",
    "moduleKey": "forge-remote-app-boot",
    "siteUrl": "https://pbray2.jira-dev.com",
    "extension": {
      "type": "macro",
      "content": {
        "id": "5341185"
      },
      "space": {
        "key": "~655362312d3308895442b0aa38771a10c88656",
        "id": "65538"
      },
      "isEditing": false,
      "references": []
    }
  },
  "principal": "655362:312d3308-8954-42b0-aa38-771a10c88656",
  "aud": "ari:cloud:ecosystem::app/8db33809-1f32-48bb-8c52-5877dab48107",
  "iss": "forge/invocation-token",
  "iat": 1700175149,
  "nbf": 1700175149,
  "exp": 1700175174,
  "jti": "d8a496253ec8c18a54631e4c82cbedd5d0ae8570"
}

Verifying remote requests

If you implement a remote backend, you take on additional responsibilities under the Forge Shared responsibility model.

You must ensure that you:

  • Validate all JWT Forge Invocation Tokens provided to your remote endpoint in an authorization header against the following JWKS to ensure that they originated from Atlassian Forge and were intended for an audience of your Application ID.

  • Never share or log any OAuth access tokens provided.

  • Avoid storing OAuth access tokens unless absolutely necessary.

To help illustrate the requirements for the above, here is some sample code in both Javascript and Java, which is available from the linked repos.

Example code in Java:

1
2
@Component
public class FITValidator {

    @Value("${jwks.endpoint:https://forge.cdn.prod.atlassian-dev.net/.well-known/jwks.json}")
    private String jwksUrl;

    @Value("${appId}")
    private String appId;

    public void validate(String invocationToken) throws InvalidJwtException {
        var httpsJwks = new HttpsJwks(jwksUrl);
        var httpsJwksKeyResolver = new HttpsJwksVerificationKeyResolver(httpsJwks);
        var jwtConsumer = new JwtConsumerBuilder()
                .setVerificationKeyResolver(httpsJwksKeyResolver)
                .setExpectedAudience(appId)
                .setExpectedIssuer("forge/invocation-token")
                .build();

        jwtConsumer.process(invocationToken);
    }
}

Example code in Javascript:

1
2
export const validateContextToken = async (invocationToken, appId) => {
  const jwksUrl = 'https://forge.cdn.prod.atlassian-dev.net/.well-known/jwks.json';
  const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl));

  const payload = await jose.jwtVerify(invocationToken, JWKS, {audience: appId});
  return payload;
}

Calling Atlassian product APIs from a remote back end

If you want to make additional API calls from your remote endpoint back to Atlassian platform or product APIs you can use the user or system token provided as a bearer token.

Confluence node client

1
2
'use strict'
import fetch from 'node-fetch';

export async function fetchFromConfluence(token, apiBaseUrl) {
  const headers = {
    Accept: 'application/json',
    Authorization: `Bearer ${token}`
  }
  return await fetch(`${apiBaseUrl}/wiki/rest/api/content`, { headers });
}

Confluence java client

1
2
public ResponseEntity<String> getContent(final String token, String baseUrl) {

    final HttpHeaders headers = new HttpHeaders();
    headers.setBearerAuth(token);

    final HttpEntity<String> entity = new HttpEntity<>(null, headers);

    final ResponseEntity<String> response =
            restTemplate.exchange(baseUrl + "/wiki/rest/api/content",
                    HttpMethod.GET, entity, String.class);

    logger.info("Response statusCode={}", response.getStatusCode());

    return response;
}

Calling the Forge Storage GraphQL APIs

Capabilities

Access to Forge storage is provided to remote back ends through GraphQL. You can get, set, update, and delete information in your app's Forge storage with GraphQL queries.

Authentication

Atlassian GraphQL APIs only accept OAuth bearer tokens as authentication. Your app must supply this token in the Authorization: Bearer {bearerToken} header of the request.

In order for the Forge platform to send the token to your remote function, you must set the auth.appSystem.token.enabled property to true for the endpoint that will access Forge Storage. Your remote function can then use the OAuth token from the x-forge-oauth-system header sent with requests to your backend endpoint.

Authorization

Your app must request the storage:app scope and the read:app-system-token scope to access Forge Storage APIs. This can be done by including them in the permissions section of your app's manifest.yml file.

Requests

All Forge Storage GraphQL requests should be HTTP POST requests to the endpoint: https://api.atlassian.com/graphql.

The request body should be JSON encoded and of the format:

1
2
{
  "query": "...",
  "operationName": "...",
  "variables": { "myVariable": "someValue", ... }
} 

Where:

Responses

The response will be in JSON format and of the shape:

1
2
{
  "data": { ... },
  "errors": [ ... ]
}

Where:

  • data contains the field requested in the GraphQL query.
  • errors is a list of errors that occurred. The field is only included if there was an error when executing the request.

Error handling for Forge Storage requests

Example response for a query that attempts to get a key from an invalid context

When making the following GraphQL query with an invalid contextAri:

1
2
query badGet {
  appStoredEntity(
    contextAri: "ari:cloud:confluence::site/bad-site",
    key: "test-key",
    encrypted: false
  ) {
    value
  }
}

The app receives the response:

1
2
{
    "errors": [
        {
            "path": [
                "appStoredEntity"
            ],
            "locations": [
                {
                    "line": 2,
                    "column": 3
                }
            ],
            "message": "Context ari:cloud:confluence::site/bad-site is not accessible in this request",
            "extensions": {
                "statusCode": 400,
                "errorType": "CONTEXT_INVALID"
            }
        }
    ],
    "data": {
        "appStoredEntity": null
    }
}

HTTP errors for GraphQL operations

Your app may also encounter HTTP errors in the event of some severe issues. For instance:

  • HTTP 401 is returned if the OAuth token provided as authorization for the request was invalid or missing

  • HTTP 400 is returned if the query was malformed (e.g - requesting for a field that doesn’t exist or using invalid GraphQL syntax)

  • HTTP 500 is returned if the GraphQL Gateway is down

Forge Storage GraphQL documentation

For examples of how to get, set, query, and delete data in Forge storage, including encrypted storage, with GraphQL from a remote back end, see Forge storage GraphQL query examples.

Observability

Metrics

Metrics on remote invocations and errors encountered are available in the developer console.

The Remote error type shows the count of remote invocation errors.

Dev console invocation errors graph showing remote invocation
errors

Logs

Remote invocations are included in the Forge logs in the developer console and the CLI.

Dev console showing remote invocation logs

Tracing

To help with diagnosing issues Atlassian will provide the following trace headers as part of each request as well as part of the invocation body. If you provide this traceId in any support correspondence with Atlassian this should help speed up the resolution of any issues.

You can also optionally join this trace on the remote endpoint side and propagate these values onto any product and platform APIs that you need to call.

HeaderExplanation
x-b3-traceidThe TraceId is 64 or 128-bit in length and indicates the overall ID of the trace. Every span in a trace shares this ID.
x-b3-spanidThe SpanId is 64-bit in length and indicates the position of the current operation in the trace tree. The value should not be interpreted: it may or may not be derived from the value of the TraceId.

Reference applications

To help with getting started we are also providing some example applications that you can use to reference when building.

Reference Forge Application:

Reference Backend Endpoints:

Known limitations and issues

Forge remote is still in preview and there are a few important improvements we’re still working on:

Improved local dev loop

For development with a local server, you will need to update the baseUrl in the remotes section of manifest.yml to the local address. You will need to redeploy your app and restart your tunnel.

Multi-region support

At the moment, all Forge invocations are executed in us-west-2 US West (Oregon) and that will be the case for remote invocations as well. We are working on multi-region compute for the Forge platform. When that is available we will also enable it for Forge remote.

Rate this page: