Developer
Get Support
Sign in
Get Support
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Last updated Jan 12, 2026

Prepare your cloud app for Forge migration

This page explains how to prepare your cloud app for migration by processing events.

Events

The Forge function and trigger modules that you implement will allow the app migration platform to send information about specific migration events that take place on your server app.

Add Forge function and trigger to the manifest

Define a function and trigger to process migration events.

See an example

1
2
modules:
  function:
    # Function to handle migration events
    - key: migration-fn-key
      handler: index.run
  trigger:
    # Migration events that will invoke your function
    - key: migration-trigger
      function: migration-fn-key
      events:
        - avi:ecosystem.migration:triggered:listener
        - avi:ecosystem.migration:uploaded:app_data

Our sample app provides a complete implementation.

Refer to the events API for all the events that you can listen for and their associated payloads.

Processing events

Unhandled exceptions or function timeouts will result in the migration event being sent again with exponential backoff up to four times for a total of five attempts.

When processing events it is important to keep in mind the following:

  • Event acknowledgement - you must call messageProcessed(transferId, messageId) for avi:ecosystem.migration:uploaded:app_data events within 15 minutes of either
    • Sending a success message for any event in a migration
    • Uploading new data from the server app.
  • Events are not ordered - the platform may send events in a different order e.g. avi:ecosystem.migration:uploaded:app_data may appear before avi:ecosystem.migration:triggered:listener.
  • Similarly, apps are not required to acknowledge events in any particular order. If they need to process data in a particular order, they may acknowledge events as processing of the contained data is completed, rather than when the message is received. This is better than acknowledging events as they are received, and then doing lengthy processing at the end, because it avoids timeouts and provides more accurate status information back to customers.
  • Validate the event payloads and throw an exception if validation fails - this will help you monitor for failed function invocations and failed function invocations will be retried.

Example timeline for event processing

TimeEventNew timeout time
00:00Migration started by customer00:15
00:01File A uploaded by DC app00:16
00:01Event for File A received and recorded by Forge App but not yet acknowledged00:16 (unchanged)
00:12File B uploaded for DC app00:27
00:13Event for File B received and recorded by Forge App but not yet acknowledged00:27 (unchanged)
00:22File C uploaded for DC app00:37
00:22Event for File C received and recorded by Forge App but not yet acknowledged00:37 (unchanged)
00:22Upload completed by DC app00:37 (unchanged)
00:22Forge App starts processing File C (e.g. because it needs to go first)00:37 (unchanged)
00:32Forge App completes processing File C and acknowledges C uploaded event00:47
00:33Forge App starts processing File A since dependency C is done00:47 (unchanged)
00:38Forge App starts processing File B00:47 (unchanged)
00:43Forge App completes processing File A and acknowledges A uploaded event00:58
00:48Forge App completes processing File B. It acknowledges File B and marks the transfer as SUCCESSN/A

Note that avoiding dependencies, and doing more importing in parallel is better than relying on a scheme like the above - but if need be sequencing processing will work, because each upload or acknowledge event occurs before the timeout.

If you are migrating from Connect and are finding it difficult to ensure you acknowledge an event or upload new data every 15 minutes without a major rewrite of your migration logic, please contact Ecosystem Support; we have some additional temporary options available (subject to approval and periodic review of your use case by Atlassian) to support migrations until you are able to progressively import and acknowledge data.

Event types

You can add the following events to your Forge trigger.

EventDescription
avi:ecosystem.migration:triggered:listenerYour server app migration listener has been triggered.
avi:ecosystem.migration:uploaded:app_dataYour server app uploaded app data.
avi:ecosystem.migration:requested:transfer_cancellationThe user has cancelled a transfer.
avi:ecosystem.migration:errored:listenerAn unhandled exception occurred in your server app migration listener.
avi:ecosystem.migration:completed:export_phasecompleteExport() has been called in your server app to signal to your Forge app that all app data has been uploaded.
avi:ecosystem.migration:settled:transferThe app migration platform has settled the transfer.

Payload

This is the data that is passed to your Forge function when it is invoked.

AttributeTypeDescription
eventTypestringThe migration event type.
transferIdstringAn ID (UUID) that the app migration platform uniquely generates per migration.
migrationDetails.migrationIdstringAn ID (UUID) that the app migration platform uses to uniquely identify a migration.
migrationDetails.migrationScopeIdstringAn ID that the app migration platform generates to uniquely determine a source (server) and destination (cloud-site) of migration.
migrationDetails.createdAtnumberTimestamp of when the app migration was created.
migrationDetails.cloudUrlstringURL of the destination cloud site for the migration.
migrationDetails.namestringThe name of the migration plan provided by the user who initiated the migration.
keystringAn ID (UUID) to uniquely identify the data your server app uploads to cloud storage. This will only be present on avi:ecosystem.migration:uploaded:app_data events.
labelstring | undefinedMetadata that provides additional information about the data your server app uploads to cloud storage. This will only be present on avi:ecosystem.migration:uploaded:app_data events.
messageIdstringAn ID (UUID) that the app migration platform generates to uniquely recognise an event.
transferError.exceptionTypestring | undefinedMetadata that provides details of the exception type that occurs when executing server side listener method (onStartAppMigration).
This will only be present on avi:ecosystem.migration:errored:listener events.
transferError.safeStackTracestring | undefinedMetadata that provides the stack trace of error that occurs when executing server side listener method (onStartAppMigration).
This will only be present on avi:ecosystem.migration:errored:listener events.

Example payload

1
2
{
  "eventType": "avi:ecosystem.migration:uploaded:app_data",
  "transferId": "3f3a47f2-a6a2-4204-84bb-d0fc504c9dc6",
  "migrationDetails": {
    "migrationId": "403c4f71-a0d1-4a63-97a8-487d18691c46",
    "migrationScopeId": "0ba07dd9-3804-4600-9102-fa6e1efeab08",
    "createdAt": 1723111376499,
    "cloudUrl": "https://your-customer-cloud-site.atlassian.net",
    "name": "Migration Plan Name"
  },
  "key": "e094ca53-3747-4541-b263-0bf7b56a5bca",
  "label": "file-label-you-used",
  "serverAppVersion": "1.0",
  "messageId": "53f88ea7-a2d2-4dd2-9f36-2d8c43401b11"
}

Forge migration API

Forge migration API helps you migrate your server app using your Forge app.

Import the migration API package and use its methods, for example:

1
2
import { migration } from "@forge/migrations";

export function getMigrationMappings() {
  return migration
    .getMappings(transferId, "jira/classic:appCustomField")
    .getMany(); // => {"results":[{"key":"10004","value":"10011"}, ...]}
}

The migration object contains helper functions to conduct your app migration, e.g., retrieve mappings and app data.

Method signature

1
2
export interface Migration {
    getMappingById: (transferId: string, namespace: string, keys: Array<string>) => Promise<MappingResponse>;
    getAppDataList: (transferId: string) => Promise<AppDataListResponse>;
    getAppDataPayload: (key: string) => Promise<APIResponse>;
    messageProcessed: (transferId: string, messageId: string) => Promise<void>;
    messageFailed: (transferId: string, messageId: string) => Promise<void>;
    getMappings: (transferId: string, namespace: string) => DefaultQueryBuilder;
    getContainers: (transferId: string, containerType: string) => DefaultQueryBuilder;
    addLog: (transferId: string, logMessage: string) => Promise<void>;
}

export interface MappingResponse {
    result: Map<string, string>;
}

export interface AppDataListResponse {
    result: Set<AppData>;
}

export interface AppData {
    s3Key: string;
    key: string;
    label: string;
}

export declare type APIResponse = Pick<Response, 'json' | 'text' | 'arrayBuffer' | 'ok' | 'status' | 'statusText' | 'headers'>;

export interface DefaultQueryBuilder {
    limit(limit: number): DefaultQueryBuilder;
    cursor(cursor: string): DefaultQueryBuilder;
    getOne(): Promise<Result | undefined>;
    getMany(): Promise<ListResults>;
}

export interface Result {
    key: string;
    value: object;
}

export interface ListResults {
    results: Result[];
    nextCursor?: string;
}

export class MigrationAPIError extends Error {
    status: number | undefined;
    message: string;
}

Parameters

NameTypeDescription
transferIdstringAn ID (UUID) that the app migration platform uniquely generates per migration.
keystringAn ID (UUID) to uniquely identify the data your server app uploads to cloud storage.
namespacestringMapping namespace to fetch mappings for.
containerTypestringType of container, valid values are ConfluenceSpace, JiraProject, Site.
keysArray<string>List of server keys to query for.
messageIdstringAn ID (UUID) to uniquely identify Forge event for the app migration platform.
logMessagestringMessage to display to customers that will be visible in progress logs.

Error handling

Any non-200 response status will result in the function throwing MigrationAPIError. You can handle it like so:

1
2
import { migration, MigrationAPIError } from '@forge/migrations'

function getMigrationMappings() {
    return migration.getMappings(transferId, namespace)
        .getMany()
        .catch((error) => {
            if (error instanceof MigrationAPIError && error.status !== undefined) {
                console.error('Unexpected status code', error.status)
            } else {
                console.error('Something went wrong')
            }
        })
}

Forge migration REST API

This documentation has moved. See REST API documentation instead.

Rate this page: