How to add Forge macros (extension) to the page programmatically via REST API

Hi!

I’ve created Forge App (Macro) for Confluence Cloud and it works fine when I add it manually via Confluence Web UI like “/myMacro”.

But I need to add it programmatically via REST API. I have a program that generates Confluence pages and their content on a trigger.

I’ve tried inspect app markup (that I’ve added manually) to get a storage format:

<ac:adf-extension>
    <ac:adf-node type="extension">
        <ac:adf-attribute key="extension-type">com.atlassian.ecosystem</ac:adf-attribute>
        <ac:adf-attribute key="extension-key">...</ac:adf-attribute>
        <ac:adf-attribute key="parameters">
            <ac:adf-parameter key="local-id">...</ac:adf-parameter>
            <ac:adf-parameter key="extension-id">...</ac:adf-parameter>
            <ac:adf-parameter key="extension-title">myMacro</ac:adf-parameter>
            <ac:adf-parameter key="guest-params">
                <ac:adf-parameter key="param1">...</ac:adf-parameter>
            </ac:adf-parameter>
        </ac:adf-attribute>
        <ac:adf-attribute key="text">myMacro</ac:adf-attribute>
        <ac:adf-attribute key="layout">default</ac:adf-attribute>
        <ac:adf-attribute key="local-id">...</ac:adf-attribute>
    </ac:adf-node>
    <ac:adf-fallback>
        ...
    </ac:adf-fallback>
</ac:adf-extension>

And add this code to the REST API call like:

POST 
...
"body": {
        "storage": {
            "value": "<ac:adf-extension>...</ac:adf-extension>",
            "representation": "storage"
        }
    }

So I’ve got:
Error loading the extension!
on the Confluence Page

My questions:

  • Is there any documentation on this topic?
  • Can I omit “local-id” somehow? As I understand it’s unique value for each instance of macro, so I need to provide it somehow via my page generation program.
  • What have I done wrong?

Please, help
Thanks!

1 Like

Hi

I’d say this is more of a question for Confluence than Forge. I’ll contact one of the teams involved to get their say on this.

1 Like

Okay, it appears to be possible.

The team recommends using the ADF (Atlassian Document Format) instead of the storage one.

In order to get an ADF representation of a page you can use
https://<SITE NAME>.atlassian.net/wiki/rest/api/content/<CONTENT ID>?expand=body.atlas_doc_format

1 Like

@JoshuaHwang

We also have the use case of wanting to add macros to a page via an API call. It is a macro that a space admin may want to add at the bottom of many pages.

Even using ADF it seems there are several unique id’s that we need to add for the macro to be added.

Some of these can be constructed from the product context - but others we cant. It would be great if the Confluence team could document how a forge macro is supposed to be represented in the ADF (connect macros are also not documented in ADF docs), and if the forge team could ensure we actually have the required id’s in the right format as part of the product context.

{
    "type": "extension",
    "attrs": {
      "layout": "default",
      "extensionType": "com.atlassian.ecosystem",
      "extensionKey": "24d93dab-91a6-48b7-b4dd-25da2154ddc3/396f4338-53eb-4e8b-b3e0-9e007afbc95b/static/<macro-name>",
      "text": "<macro-name> (Development)",
      "parameters": {
        "localId": "d5bf3705-e831-4ab4-9277-59a4dfc3f2ed",
        "extensionId": "ari:cloud:ecosystem::extension/24d93dab-91a6-48b7-b4dd-25da2154ddc3/396f4338-53eb-4e8b-b3e0-9e007afbc95b/static/<macro-name>",
        "extensionTitle": "<macro-name>"
      },
      "localId": "8b8113b9-732b-41a3-ad51-2f0894745542"
    }

@vkochugova - you marked the solution above. Is it possible to explain how you actually got it to work?

1 Like

@richard.white When using the ADF format (atlas_doc_format) you can omit the localId params.

Example: Outline of request body using Python

body = {
    "spaceId": "123456", 
    "title": "Adding extension via REST API (ADF)",
    "body": {
        "representation": "atlas_doc_format",
        "value": json.dumps({
            "type": "doc",
            "content": [
                {
                    "type": "paragraph",
                    "content": [{"text": "Some text before extension..", "type": "text"}]
                },
                {
                    "type": "extension",
                    "attrs": {
                        "layout": "default",
                        "extensionType": "com.atlassian.ecosystem",
                        "extensionKey": "...",
                        "text": "Some extension",
                        "parameters": {
                            "guestParams": {
                                "key": "value",
                            },
                            "extensionId": "...",
                            "extensionTitle": "Some extension"
                        }
                    }
                },
            ]
        })
    }
}

res = requests.post("https://example.com/wiki/api/v2/pages", headers=headers, json=body)
2 Likes