Page content status / get and set via REST API

Is there a way to get/set the new page content statuses via REST API?

I do see we can get the status with expand=metadata.properties.content_state_draft,metadata.properties.content_state_published

It returns some interesting-looking JSON values. In this example, it’s a custom “Published” status.

Obtained with this GET call: /wiki/rest/api/content/835059848?expand=metadata.properties.content_state_draft,metadata.properties.content_state_published

Result:

Any official API for this, to get all available content status etc?

  • Why are there 2 statuses content-state-published, and content-state-draft? In my tests both were populated with the same value. (but different value ‘versions’)
  • Getting the value and parsing it as JSON could work, but not sure if Atlassian intended this
  • Setting the value to this undocumented JSON value does seem a bit shaky
2 Likes

Additional questions appear related to our App Content Property Editor.

Usually, content properties are stored as valid JSON; thus, you can display single properties using a JSON path. However, the JSON value for both content-state-published and content-state-draft are not valid JSON… they are escaped.

Can someone on Atlassian’s side comment on this? Is it a bug?

1 Like

Hi Lukas,

We do have dedicated endpoints for getting and setting content state, and they can be found here:

Get: Get Content State

Set: Set Content State

The content-state-published property will appear on the view page, and the content-state-draft will appear in the draft. When the page is published, a published property identical to the latest draft property will become the published property. The set endpoint above will automatically publish the page if the status query param is set to current.

I’ll look into the escaped JSON and the reasoning for it, or if it’s a bug.

Hope this helps!

Emile

1 Like

@LukasGotter If I understand the situation correctly, you can set the page state through the API, but you can’t set the page status through the API.
Confusingly it seems the page status is called contentState in the JSON.

Hi Marc, there are 2 different concepts related to a page status:

  • Page Status: Available in the /wiki/rest/api/content endpoints. Valid values: current, deleted, historical, draft
  • Page Content Status: The new API referenced in the answer above by @EmileGivental

@LukasGotter Indeed. The endpoints available through wiki/rest/api/content are about the page state.
Page (content) status is newer, but the confusing thing is that it is called contentState and not contentStatus in the JSON.

@marc in which JSON do you get an attribute contentState?

@LukasGotter contentState is in the quoted JSON, i.e. if you GET e.g. metadata.properties.content_state_published
So there is the state like draft, published. And there is the page (content) status in quoted JSON, which is called contentState.

Oh, I see, true. Not sure how reliable that content property with quoted json is. Let’s see @EmileGivental feedback on that.

Well but then we can summarize:

  • “content state” = the new customizable page content state, and
  • “status” = the page status (draft, current, deleted, archived, …).

Hi @LukasGotter If I understand correctly, it’s the other way round:

page “state” = the page status (draft, current, deleted, archived, …)
content “status” = the new customizable page content state

Hmm @marc I don’t think so.
If we look at the official REST API (and forget about the unofficial content property where the content state is saved and named contentState) then there are 2 APIs:

  • Page Status (property status, accessible via wiki/rest/api/content/{id})
  • Page Content State (property contentState, accessible via wiki/rest/api/content/{id}/state)

Confusion is however created because the feature is announced as “page status” :laughing:

Hello @LukasGotter. I came across this post while experimenting with your content properties editor and macros. I’ve managed to display the current content status (state) value using a single content property macro like this:
image

However - I was wondering if you’ve figured out a way to get the name of the user who last modified the content status? That would be quite useful in page property reports, for example. I’m thinking the “setterAAID” element might be a user identifier? Any idea if/how that could be converted to a user name?? If not - maybe that’s an enhancement of some sort you can consider for the content property macro?

  {
    "id": "3752263776",
    "key": "content-state-published",
    "value": "{\"contentState\":{\"id\":3554246679,\"color\":\"#57d9a3\",\"name\":\"Ready for review\"},\"version\":19,\"isSpaceState\":true,\"isNewState\":false,\"setterAAID\":\"5f88a743b2964c006e7a43e4\"}",
    "version": {
      "when": "2023-01-11T18:14:52.844Z",
      "message": "",
      "number": 9,
      "minorEdit": false,
      "contentTypeModified": false
    },
    "_expandable": {
      "content": "/rest/api/content/3538518152"
    },
    "_links": {
      "self": "https://urbint.atlassian.net/wiki/rest/api/content/3538518152/property/content-state-published"
    }
  },

Also - I guess your macro only returns the “value” element. Would there be a way to get the “version” (and specifically, the “when” element)? That would allow a page property report to show the current content state, along with when it was set and who set it!

The issue with the escaped property has been fixed on our side by supporting both escaped and unescaped JSON values.

@TonySileo Yes, you can also display the version of the content state. Use “$.version” to display the version. Currently, only raw values are possible but if there is enough interest we might allow different renderings (user, dateTime, …). Let us know your use case. Hope this helps!

Thanks @LukasGotter. yes - I figured I could get the version and setterAAID, but my use case is to add a Page Property macro where the value for certain page properties is extracted from the content properties, and then can be displayed in a page properties report that shows status, status change date, and who changed the status for an entire hierarchy of labeled pages. Something like this:

Except the Status Changed By would be much more useful as a person’s name or link to their profile.
As I think about this more, I also realize the version, date, and setterAAID from the current content properties will always represent the most recent change to the page, which might not be when the page’s content status (state) was last changed. I guess the only way to get that would be by trolling through the page history to find when it changed, and then grabbing the version, date, and setterAAID from that version of the page. That’s probably a bigger ask - so I get it if you can’t support that!

I was experimenting a bit around this. Would an option to choose between the value and version property solve your case?

Resolving the user’s full name would require the app to use additional permission to access the user API (doable but needs to be verified and tested).

Are you sure the content state value and version attributes will be updated with a new version number, even if they there is no change to the content state? Not sure about the history, do you know how to access it? A macro to display the content state alongside the person who changed it would indeed be useful. @TonySileo

Page history is available in the … menu:
image

I just confirmed that the version number in the content-state-published property increments each time someone publishes a new version of the page, regardless of whether the content state (status) changes.

Also just noticed that the content property macro doesn’t work correctly when viewing previous versions of a page:
image

Here is my pycode for this:

def set_page_status( page_id, new_status):
    """
    Sets the status of a Confluence page.

    Args:
    
        page_id (str): ID of the page whose status you want to update.
        new_status (str): The new status to set for the page.

    Returns:
        str: A message indicating success or failure.
    """
    try:
        url = f"{base_url}/rest/api/content/{page_id}/state?status=current"
        print(url)
        headers = {
        "Accept": "application/json",
        "Content-Type": "application/json"
        }
        data = json.dumps({
            
                #"id": ####, #not required
                "color": "#57d9a3",
                "name": new_status
            
            } )
        response = requests.put(url, data=data, auth=(username, password), headers=headers)
        print("response post", response)
        print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
        if response.status_code == 200:
            return f"Page status updated successfully. New status: {new_status}"
        else:
            return f"Error: Unable to update page status (HTTP {response.status_code})"
    except Exception as e:
        return f"Error: {str(e)}"

and


def get_page_status(page_id):
    
    
    try:
        url=f"{base_url}/rest/api/content/{page_id}/state"
        headers = {
        "Accept": "application/json"
        }
        response = requests.request(
        "GET",
        url=url,
        headers=headers,
        auth=auth2
        )
        #print("response 'get':"   , response)
        print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
        if response.status_code == 200:
            page_data = response.json()
            #print(page_data)
            if page_data:
                #print(page_data['contentState']['name'],page_data['contentState']['color'])
                return page_data['contentState']['name'],page_data['contentState']['color']
            else:
                return -1

        else:
            return f"Error: Unable to retrieve page status (HTTP {response.status_code})"
    except Exception as e:
        return f"Error: {str(e)}"