Skip to main content

Publishing to Atlassian Confluence (with Python)

Introduction

This article shows how to automate the interaction with Atlassian Confluence, a commercial product widely used in companies to publish documentation and other content for a team, a department or the whole organization. It is usually integrated with other Atlassian tools such as Bamboo and JIRA.

By “automating the interaction” I mean automated document publishing: you extract data from other systems with automated tools, then publish it in a readable format. This is possible through the REST interface that Atlassian provides for its products. REST does not cover every feature, but it is enough for this scenario. The reference for Confluence Cloud is here. The interface can differ between versions, so check the API reference for your own Confluence version.

Now that we have the tools and the method, let’s get to the implementation, and to some code.

Prerequisites

First, create the document the classic way, with the “Create” button in the web interface. A document can be created through REST calls, but you would need to supply the “page key” and the “parent page id”, which are easy to read from the interface. Once the document exists, note down its page key and page id.

  • The page key is visible in the page URL.
  • The page id becomes visible when you edit the page.

Document Retrieval

All the code below assumes a dictionary holding the credentials and the document identifiers:

confluence_data = {
    "server": "xxx",
    "user": "xxx",
    "pass": "xxx",
    "page_key": "xxx",
    "page_id": "xxx"
}

Confluence does not accept an update unless the version number (an integer) is incremented. So you must first retrieve the document (as JSON, here) to read the current version, then increment it:

import requests
import json

def confluence_retrieve(confluence_data):
    url = ('https://' + confluence_data['user'] + ':' + confluence_data['pass'] +
           '@' + confluence_data['server'] + '/confluence/rest/api/content/' +
           confluence_data['page_id'])
    headers = {'X-Atlassian-Token': 'no-check', 'content-type': 'application/json'}
    response = requests.get(url, headers=headers, verify=False)
    return int(response.json()['version']['number'])

Document Update

Updating a document takes a few steps:

  1. Format the content in a representation Confluence accepts (details below).
  2. Generate a new version number by incrementing the old one.
  3. Build the payload (JSON here; XML also works).
  4. Send the PUT request and hope for the best.
def confluence_update(confluence_data, new_title, new_content):
    old_version = confluence_retrieve(confluence_data)
    new_version = old_version + 1

    url = ('https://' + confluence_data['user'] + ':' + confluence_data['pass'] +
           '@' + confluence_data['server'] + '/confluence/rest/api/content/' +
           confluence_data['page_id'])
    data = {
        "id": confluence_data['page_id'],
        "type": "page",
        "title": new_title,
        "space": {"key": confluence_data['page_key']},
        "body": {
            "storage": {
                "value": new_content,
                "representation": "wiki"
            }
        },
        "version": {"number": str(new_version)}
    }
    headers = {'X-Atlassian-Token': 'no-check', 'content-type': 'application/json'}
    response = requests.put(url, headers=headers, data=json.dumps(data), verify=False)
    return

Confluence accepts several content representations:

  • plain: plain text, used as provided with no special formatting.
  • wiki: Confluence wiki markup. This is not the Wikipedia wiki markup but a different format. It is the easiest way to get nicely formatted documents.
  • storage: an HTML document, as it is meant to be displayed in the browser.

Conclusion

Automated document updating is a powerful way to distribute up-to-date information, such as service versions or usage patterns. As infrastructure grows, it may be the only way to keep support effort scaling sub-linearly with infrastructure size. That’s it for today. Thanks for reading!