Last updated Apr 14, 2024

Working with a single-bodied macro

This tutorial primarily focuses on single-bodied macros, specifically those with either a "rich-text" or "plain-text" body type. For information and guidance on multi-bodied macros, please refer to our separate tutorial dedicated to that topic.

A macro body is content that the user can edit in your macro. Working with the macro body is useful if your macro is designed to format content. To get the macro body, your app needs to use macro variables in a REST API call.

By the end of this tutorial, you will:

  • add a dynamicContentMacros module with a body type and URL parameters
  • add a view with a body to render
  • call the Get macro body by macro ID endpoint to retrieve the macro body

Let's get started!

Before you begin

Ensure you have installed all the tools you need for Confluence Connect app development by Getting set up with Atlassian Connect Express (ACE):

  1. Get a cloud development site.
  2. Enable development mode in your site.
  3. Install ACE.

Before starting this tutorial, it is helpful to complete the previous tutorial: Creating a dynamic content macro.

Create a new app

Start by creating a fresh app to work with.

  1. From the command line, go into a directory where you'd like to work, and type:
    atlas-connect new macro-body-tutorial
  2. Select Confluence in the menu.
  3. When the command finishes, go into the macro-body-tutorial directory and run the following command to install any dependencies for the app: npm install
  4. Add the credentials.json file that gives your app permission to work with your Confluence Cloud development site. Copy the one you created during the Getting started tutorial. You can ignore or delete the existing credentials.json.sample file.

Add a macro

Add a dynamicContentMacros object inside the modules block of your app descriptor:

"modules": {
        "dynamicContentMacros": [{
            "url": "/myMacro?macroId={}&pageId={}&pageVersion={page.version}",
            "description": {
                "value": "Allow users to add a background color around selected content."
            "bodyType": "rich-text",
            "name": {
                "value": "My macro"
            "key": "my-macro"

Take note of the following fields:

  • url: In addition to the endpoint, the URL includes parameters to capture the macro variables,, and page.version, which are required for the REST API call to retrieve the macro body.
  • bodyType: This is a required field for any macro that provides a body.

For a list of macro variables, see Dynamic content macro.

Add a view

Create a file called my-macro.hbs in the views directory:

{{!< layout}}
    {{#if body}}
        Here is a preview of your macro!

This simple layout displays the macro body if it exists.

Add a route handler

The route handler takes the variables passed in from the URL and uses them to make a REST API call to retrieve the macro body. For more information, see Get macro body by macro ID.

In routes/index.js, add a new app.get() method under the // Add additional route handlers here… comment:

app.get('/myMacro', addon.authenticate(), function(req, res){

        // Get the macro variables passed in via the URL
        const pageId      = req.query['pageId'],
            pageVersion = req.query['pageVersion'],
            macroId     = req.query['macroId'];

        // Get the clientKey and use it to create an HTTP client for the REST API call
        const clientKey = req.context.clientKey;
        const httpClient = addon.httpClient({
            clientKey: clientKey  

        // Call the REST API: Get macro body by macro ID
            '/rest/api/content/' + pageId +
            '/history/' + pageVersion +
            '/macro/id/' + macroId,
            function(err, response, contents){
                if(err || (response.statusCode < 200 || response.statusCode > 299)) {
                    res.render('<strong>An error has occurred : '+ response.statusCode +'</strong>');
                contents = JSON.parse(contents);

        // Render the view, passing in the {{{body}}}
        res.render('my-macro', {
            body: contents.body

Notice how the macro variables are used to construct the REST API call. For an example of a slightly different way to make the call, see the Properties section of the Dynamic content macro reference page.

Test the app

Now it's time to see the macro body in action:

  1. From the command line, go into the macro-body-tutorial directory and type npm start to start the app.
  2. Edit a page in your Confluence Cloud developer site.
  3. Type /my and click My macro.
  4. Type some text in the box.
  5. Publish the page.

When you load the page, you see the macro body, rendered as text on the page. On the command line, you see the parsed contents of the macro, output by the statement console.log(contents); in the route handler. It should look something like this:

  name: 'my-macro',
  body: '<p>First test of my macro content!</p>',
  parameters: {},
  _links: { base: 'https://<your dev instance>', context: '/wiki' }

You can also see the GET call to your handler, including the populated variables:

GET /myMacro?macroId=729c5f82-f62c-465b-b010-31777343d357&pageId=358416385&pageVersion=1& 200 402.004 ms - 1391


Once you have access to contents.body you can do more than just render it in a view: you can change it, display it elsewhere, or use it like any other string.

If you decide to use a static content macro instead of a dynamic content macro, you can get the macro body the same way. The only difference is that you declare staticContentMacros instead of dynamicContentMacros in the app descriptor.

Rate this page: