How to Purge All (remove all trash in a space) using REST API

Still need help?

The Atlassian Community is here for you.

Ask the community

Platform notice: Server and Data Center only. This article only applies to Atlassian products on the Server and Data Center platforms.

Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.

*Except Fisheye and Crucible

    

Summary

When content is deleted, it is often not outright removed from the instance. The majority of the content in Confluence (pages, blogposts etc) are trashable. That means that when deleted they will be flagged as trashed and remain in the instance, attached to their space, until a space administrator purges this content.

For pages and blogposts, this can also be done for individual content with a DELETE request over the REST API. The end-point for that is:

DELETE /rest/api/content/{id}

We simply need to inform the request that this content is trashed, using the relevant switch which is:

status=trashed

So for example, the below command using curl and basic authentication will delete a trashed page:

curl -u {username}:{password} -X DELETE "{BASE_URL}/rest/api/content/{id}?status=trashed"

An interesting question then is: how can we purge all trashed content from a space using the REST API?

Diagnosis

In order to purge all trashed content from a space, since there is no purge all end-point, we have to use a more indirect method. We need to identify the trashed content associated with a space and using recursion send a DELETE request to each of them.

To identify purge content associated with a space we can use a GET request towards api/content. The switches should limit the content we get to that of a specific space, since that's what we want, and of the trashed status. So they should be for pages:

spaceKey={KEY}&status=trashed

And for blogposts:

spaceKey={KEY}&status=trashed&type=blogpost

Unfortunately this endpoint does not currently support an attachments type.

Warning

Please pay attention to the capitalisation of switches. For instance spaceKey needs to be written exactly as shown, with K as a capital letter 

The response will be a JSON that contains the information of the trashed content. A response will look like this:

{"results":[{"id":"4947978","type":"page","status":"trashed","title":"Test 12345","extensions":{"position":"none"},"_links":{"webui":"/display/IN/Test+12345","edit":"/pages/resumedraft.action?draftId=4947978&draftShareId=c946d1fe-160a-44ce-898f-12563cf81479","tinyui":"/x/CoBL","self":"http://localhost:8090/rest/api/content/4947978?status=trashed"},"_expandable":{"container":"/rest/api/space/IN","metadata":"","operations":"","children":"/rest/api/content/4947978/child","restrictions":"/rest/api/content/4947978/restriction/byOperation","history":"/rest/api/content/4947978/history","ancestors":"","body":"","version":"","descendants":"/rest/api/content/4947978/descendant","space":"/rest/api/space/IN"}},{"id":"4947980","type":"page","status":"trashed","title":"Test-dash","extensions":{"position":"none"},"_links":{"webui":"/display/IN/Test-dash","edit":"/pages/resumedraft.action?draftId=4947980&draftShareId=3c5d3dd8-bf79-44b7-8105-0e117c06ddf8","tinyui":"/x/DIBL","self":"http://localhost:8090/rest/api/content/4947980?status=trashed"},"_expandable":{"container":"/rest/api/space/IN","metadata":"","operations":"","children":"/rest/api/content/4947980/child","restrictions":"/rest/api/content/4947980/restriction/byOperation","history":"/rest/api/content/4947980/history","ancestors":"","body":"","version":"","descendants":"/rest/api/content/4947980/descendant","space":"/rest/api/space/IN"}},{"id":"4947982","type":"page","status":"trashed","title":"BR500-100NAS","extensions":{"position":"none"},"_links":{"webui":"/display/IN/BR500-100NAS","edit":"/pages/resumedraft.action?draftId=4947982&draftShareId=aed89c94-baa9-4ae5-ab70-b23d7698f194","tinyui":"/x/DoBL","self":"http://localhost:8090/rest/api/content/4947982?status=trashed"},"_expandable":{"container":"/rest/api/space/IN","metadata":"","operations":"","children":"/rest/api/content/4947982/child","restrictions":"/rest/api/content/4947982/restriction/byOperation","history":"/rest/api/content/4947982/history","ancestors":"","body":"","version":"","descendants":"/rest/api/content/4947982/descendant","space":"/rest/api/space/IN"}}],"start":0,"limit":25,"size":3,"_links":{"self":"http://localhost:8090/rest/api/content?spaceKey=IN&status=trashed","base":"http://localhost:8090","context":""}}

Of particular interest is the field self as that contains a reference link to the trashed content itself. Isolating those links and sending DELETE to them, in the same way as it was shown above, will clear the trashed content of this space.

Solution

With all this in mind we can create a short script that will do this for us. The isolation of the self can be done in numerous ways, with bash, awk, python, etc. Below I present a sample 2 line script that can do this:

curl -u {username}:{password} -G "{BASE_URL}/rest/api/content?spaceKey={KEY}&status=trashed" | json_pp | grep -i self | grep -vi space | awk -F \" '{print $4}' > {output_file}
while read line; do curl -u {username}:{password} -X DELETE "$line"; done < {output_file}; rm {output_file}

Likewise for blogposts:

curl -u {username}:{password} -G "{BASE_URL}/rest/api/content?spaceKey={KEY}&status=trashed&type=blogpost" | json_pp | grep -i self | grep -vi space | awk -F \" '{print $4}' > {output_file}
while read line; do curl -u {username}:{password} -X DELETE "$line"; done < {output_file}; rm {output_file}

Alternative

If you don't have json_pp you can also use python to call an equivalent tool with python -mjson.tool

Addenda

Please note that the REST endpoint used in the above script has a default limit of 25 result items. If there are more than 25 objects in trash that need to be removed, then either that script will need to be run more than once, or a different limit should be specified by using the limit switch. For example, the below sample script will handle up to 100 trashed pages at once:

curl -u {username}:{password} -G "{BASE_URL}/rest/api/content?spaceKey={KEY}&status=trashed&limit=100" | json_pp | grep -i self | grep -vi space | awk -F \" '{print $4}' > {output_file}
while read line; do curl -u {username}:{password} -X DELETE "$line"; done < {output_file}; rm {output_file}

Likewise for blogposts:

curl -u {username}:{password} -G "{BASE_URL}/rest/api/content?spaceKey={KEY}&status=trashed&type=blogpost&limit=100" | json_pp | grep -i self | grep -vi space | awk -F \" '{print $4}' > {output_file}
while read line; do curl -u {username}:{password} -X DELETE "$line"; done < {output_file}; rm {output_file}

If there is many more pages/blogposts in trash, many hundreds or thousands, then a more programmatic approach should be taken. For example the above logic can be wrapped into a loop, an example bash script would be the below:

Sample Script
#!/bin/bash

BASEURL="<INSERT_BASE_URL_HERE"
SPACEKEY="<INSERT_SPACE_KEY_HERE>"
USERNAME="<INSERT_USERNAME_HERE>"
PASSWORD="<INSERT_USERPASS_HERE>"

echo "Started :" $(date)
echo "Checking for pages"
curl -u $USERNAME:$PASSWORD -G "$BASEURL/rest/api/content?spaceKey=$SPACEKEY&status=trashed" > results.txt
grep "\[\]" results.txt
exit_code=$?;

if [[ $exit_code == 0 ]] 
	then
		echo "No trashed pages found"
		rm results.txt
fi

while [[ $exit_code != 0 ]]
	do 
	echo "Deleting:" 
	echo $(cat results.txt)
	curl -u "$USERNAME:$PASSWORD" -G "$BASEURL/rest/api/content?spaceKey=$SPACEKEY&status=trashed" | json_pp | grep -i self | grep -vi space | awk -F \" '{print $4}' > list.out;
	while read -r line; do echo " purging " "$line"; curl -u "$USERNAME:$PASSWORD" -X DELETE "$line"; done < list.out;
	rm list.out
	curl -u "$USERNAME:$PASSWORD" -G "$BASEURL/rest/api/content?spaceKey=$SPACEKEY&status=trashed" > results.txt
	grep "\[\]" results.txt
	exit_code=$?
done;

echo "Checking for blogposts"
curl -u "$USERNAME:$PASSWORD" -G "$BASEURL/rest/api/content?spaceKey=$SPACEKEY&status=trashed&type=blogpost" > results.txt
grep "\[\]" results.txt
exit_code=$?; 

if [[ $exit_code == 0 ]] 
        then 
                echo "No trashed blogposts found"
                rm results.txt
fi

while [[ $exit_code != 0 ]]
        do 
        echo "Deleting:" 
        echo $(cat results.txt)
        curl -u "$USERNAME:$PASSWORD" -G "$BASEURL/rest/api/content?spaceKey=$SPACEKEY&status=trashed&type=blogpost" | json_pp | grep -i self | grep -vi space | awk -F \" '{print $4}' > list.out;
        while read -r line; do echo " purging " "$line"; curl -u "$USERNAME:$PASSWORD" -X DELETE "$line"; done < list.out;
        rm list.out
        curl -u "$USERNAME:$PASSWORD" -G "$BASEURL/rest/api/content?spaceKey=$SPACEKEY&status=trashed&type=blogpost" > results.txt
        grep "\[\]" results.txt
        exit_code=$?
done;

rm results.txt

echo "Ended :" $(date)

Please note this is not meant to be a definitive solution, simply an example of how it can be done, edit and amend according to your needs.

Last modified on Jun 9, 2022

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.