RFC-142: Confluence Migration Content Index for Macros

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

We propose providing an export of a content index referenced by macro names to Apps during Confluence migrations to help them identify which pages or blog posts require macro transformations without querying every page.

  • Publish: 10 August 2026

  • Discuss: 24 August 2026

  • Resolve: 7 September 2026

Problem

During Confluence migrations, Apps often need to transform the macros found on migrated pages and blog posts. Their macros are stored in the pages and blog posts and due to that, Apps had no way to retrieve which pages had macros they were interested in.

Because of this, Apps during migration currently resort to querying every page on the migrated Confluence instance to perform their required transformation. Depending on the number of pages migrated, this can cause a significant delay (due to request volume an rate-limiting) in processing a migration.

If there was a way of retrieving references to the pages and blog posts a specific macro exists on, it would reduce the amount of requests an App would need to perform its required transformations. This would reduce migration time for the customer.

Proposed Solution

We are considering having apps declare what macros they are interested in knowing the page and blog post references for in their MigrationListener App migration platform via the ConfluenceAppCloudMigrationListenerV1 interface similar to P2 to Forge Macros .

public class ConfluenceAppMigListener implements DiscoverableForgeListener, ConfluenceAppCloudMigrationListenerV1 {
  ...  
  @Override
    public Map<String, String> getMacrosToIndex() {
        return new HashSet<>(Set.of("macroname-1","macroname-2"));
    }
}
public interface ConfluenceAppCloudMigrationListenerV1 {
    ...
    default Set<String> getMacrosToIndex() {
        return new HashSet<>();
    }
}

The proposed approach involves indexing pages and blog posts by the macros declared on an App’s ConfluenceAppCloudMigrationListenerV1. The export of the proposed index can be retrieved (via a public presigned url) by the App to utilise during the app’s migration.

The propose format for that index export is:

export.ndjson

{"macroname": "macroname-1", "contentIds": ["2001", "2002", "2003",...]}
{"macroname": "macroname-1", "contentIds": ["3004", "3005", "3006"....]}
{"macroname": "macroname-1", "contentIds": ["4007", "4008", "4009"]}
{"macroname": "macroname-2", "contentIds": ["2011", "2012", "2013"]}
{"macroname": "macroname-2", "contentIds": ["2014", "2015", "2018"]}

The contentIds array may contain 0-1000 items

The same macroname can appear on multiple lines but will be next to each other

macroname that aren’t declared by the App may appear in the index, it is expected that the App disregards those entries.

Asks

While we would appreciate any reactions you have to this RFC (even if it’s simply giving it a supportive “Agree, no serious flaws”), we’re especially interested in learning more about:

  1. What do you think about the format of the index file?

  2. What do you think about declaring the macros in the migration listener?

    1. What do you think about declaring the macros in the Forge app’s manifest?
  3. What would your App do during migration if it knew which pages and blog posts macros were on?

  4. When does your App need to know the pages and blog posts a macro is on?

  5. Are there other ways this may impact that we haven’t anticipated?

  6. What do you think about when the index can be queried?

    1. As an webhook event during the app migration, when the index is available
    2. The index is created before the app migration is started (with the trade off that there will be a delay in starting the app migration)

Hi David.

That’s a good idea, and it is essentially what we have been doing all this time.

We started with a CQL search on the cloud side ?cql="macro = requirement and not isMigrated" where isMigrated is a custom property we add(ed) on the migrated pages. We had an issue with the delay of the indexing in CQL that we solved by adding an intentional delay on the indexing.

With Forge, we’ve done this search on the DC instance and send one AppData message per page containing a macro.

  1. What do you think about the format of the index file?

I’m wondering what would be the benefits of using ndjson vs regular json.

  1. What do you think about declaring the macros in the migration listener?

That’s great, but some customers are unable to update their DC apps before migrating, which would be a blocker for the adoption of this new listener.
2.1. What do you think about declaring the macros in the Forge app’s manifest?

Sounds ideal.

  1. What would your App do during migration if it knew which pages and blog posts macros were on?

We’ve changed the macro format from DC to Cloud from a single macro “requirement” with a type that could be “DEFINITION” or “LINK” to 2 different macros on the cloud. We update the pages to reflect this change.
Some macros reference entity Ids that need to be updated.

  1. When does your App need to know the pages and blog posts a macro is on?

At any point before the customers start to use the app.

  1. Are there other ways this may impact that we haven’t anticipated?
  • I’m not clear on how the app is signaled that it can start querying for this index export.
  • How does the contentIds interact with the various timeouts for the forge backend functions? We cannot handle the whole export in one invocation. Do we send an event that a contentId has been processed successfully? Are we responsible for maintaining a separate index up-to-date with what has already been done, and what remains (and what errored?)?

@Corentin Glad to hear this feature resonates with you! Thanks for your comments.

  1. What do you think about the format of the index file?
    I’m wondering what would be the benefits of using ndjson vs regular json.

Good question, the ndjson format would allow the data to be streamed and parsed for only the relevant data and as such reduce the amount of memory usage of your process.

What are your thoughts on the the format?

  1. What do you think about declaring the macros in the migration listener?
    That’s great, but some customers are unable to update their DC apps before migrating, which would be a blocker for the adoption of this new listener.
    2.1. What do you think about declaring the macros in the Forge app’s manifest?
    Sounds ideal.

This is all very helpful feedback.

  1. Are there other ways this may impact that we haven’t anticipated?
  • I’m not clear on how the app is signaled that it can start querying for this index export.

Thanks for asking about when you can start querying for the index. We are hoping to be informed App’s use cases before deciding on a method. At any point before the customers start to use the app. is a useful information for this regard. We currently we envisage two potential ways.

  1. As an webhook event similar to the app_data event
  2. The index is created before the app migration is started (with the trade off that there will be a delay in starting the app migration)

What do you think about those options?

I will update the RFC asking for feedback on those options

  • How does the contentIds interact with the various timeouts for the forge backend functions? We cannot handle the whole export in one invocation. Do we send an event that a contentId has been processed successfully? Are we responsible for maintaining a separate index up-to-date with what has already been done, and what remains (and what errored?)?

The contentIds are references to the pages/blog posts where the macro exists, we would propose the macros are updated by your currently implement process.

The index would only be valid at the time of migration.

That makes sense. Will there be an @forge/migrations sdk function for accessing this index?

I think both options could work, however, from my perspective, the ideal solution would be more along the lines of a stream of multiple events, each referencing one or multiple contentIds, not an API endpoint that we have to query.

-> Start migration
-> IndexEvent1 {"macroname": "macroname-1", "contentIds": ["2001", "2002", "2003",...]}
-> IndexEvent2 {"macroname": "macroname-1", "contentIds": ["4007", "4008", "4009"]}
-> AppDataEvent1
-> IndexEvent3 {"macroname": "macroname-2", "contentIds": ["2011", "2012", "2013"]}
-> AppDataEvent2
// ...

-> IndexEventEnd : `Sent N events, make sure that you've received everything`

-> MigrationSettled

I would imagine that the index would be created during the Space(s) migration step, and therefore ready by the time the app migration starts, but I don’t think that it would be a big deal to have a delay before the app migration is started. The migrations often involve multiple teams and several weeks of preparation and verification, a delay in the index creation is a small blip in that whole process.

Hi David.

Thanks for this RFC, this would be useful for our migration.

What do you think about the index format?

NDJSON works well, particularly for large migrations, as it supports streaming. Access through @forge/migrations would be helpful, since the related mapping APIs already live there.

Declaring macros in the migration listener?

This would work, but depends on the installed DC app version.

Declaring macros in the Forge manifest?

This is our preferred option. It is Cloud-owned and independent of the DC app version.

What would your app do with the index?

We already collect equivalent information during migration. The index would allow us to remove that discovery code and perform the remaining processing in Cloud.

When is the information needed?

Once the referenced pages and blog posts are accessible in Cloud. We expect the index to contain Cloud content IDs — if it contained DC IDs instead, we would additionally need the ID-mapping API to resolve them, so Cloud IDs directly in the index are strongly preferred. No mapping back to DC IDs is required on our side.

Other potential impacts?

Indexes may become large. The API should therefore support streaming or pagination, retries and resumability within Forge runtime limits. If retrieval is via presigned URL, the URL should be re-issuable and support range requests so large downloads can be resumed. Including the content type (page vs. blog post) in each line would avoid additional REST calls.

When should the index be queryable?

We prefer it to be available before the app migration starts. The additional delay is acceptable and provides a clear, deterministic processing point.

Finding our macros post-migration currently means scanning every page, having exported the entire pageID mapping from DC for every user.

1. Declaration: getMacrosToIndex() depends on whatever DC app version is installed at migration time. Customers leaving DC rarely update apps first, so the migrations that most need this will run without the listener. Macro ownership is already declared on the cloud side, so manifest-based declaration is very important, with the DC interface as an optional override.

2. Post-transfer access: please make the index retrievable after the migration window has closed too, by the installed cloud app or via an admin-triggered export like the migrations/reports/v1/id-mappings API. Content-first migrations where the app is installed or selected later are the norm in our support queue. confluence:* mappings are retained for 2 years but return 403 once the transfer settles, we need the data for 2 years, really.

3. +1 to cloud IDs in the index. Finding macros is half the job; rewriting the page references inside macro bodies and attached diagram data (which link fixing skips) still needs the server to cloud page mapping. A bulk page-mappings file alongside the index, like the JCMA mappings exporter that CCMA currently lacks, stop 18 hours of exporting the values from DC during migration.

Glad to hear this change/project resonates with you! Thanks for your comments.

Yes, they would be cloud Ids.

Glad to hear this change/project resonates with you! Thanks for your comments.

Thanks for the feature request, @daviddrawio! As we continue to explore the solution, we’ll weigh whether or not this is something that is in scope for this version. We’ll address it in the RFC Resolution.