Confluence CSV Format

Last modified by Raphaël Jakse on 2026/08/14 16:19

Content

Reference

Scope

As of version 9.96.0 (9.96.2 really, as previous versions are seriously flawed), the Confluence module can import Confluence Cloud CSV backups. This document presents their format. It is targeted at:

  • technical people working on Confluence migration tools or tools involving Confluence exports
  • technical people running migrations who need to deeply investigate some issues
  • people curious about the Confluence export format

Introduction

In 2026, Atlassian has been phasing out Confluence XML backups on Confluence Cloud. CSV backups replace them.

Like a Confluence XML package, a Confluence CSV package is a zip file. It contains, among other things, an attachments folder and a CSV file per database table (with a csv.gz extension - mind you, that doesn't always mean they are actually gzipped, see below). While the Confluence XML backup is a hibernate dump, where what's exported is serialized java objects (more or less), CSV files are more directly what's stored in the RDBMS (so for instance if you have ever looked at a PostgresSQL instance containing Confluence server stuff, these CSV file will look quite familiar). Also contrary to Confluence XML backups, there doesn't seem to be an exportDescriptor.properties file: of course, we know where a CSV export comes from: Atlassian's Confluence Cloud.

Like for Confluence XML exports, there are two types of CSV bakups we know about:

  • A space backup package, produced from the settings of a space in Confluence.
  • A site backup package, produced from the global administration in Confluence. It contains all the  spaces, as well as users and groups, but it doesn't necessarily contain the attachments folder.

At the time of writing, there are a few major differences between the site export and the space export, effectively making them slightly different formats.

  1. About the CSV tables:
    • In space backups, the CSV files are at the root of the ZIP archive, and despite their .csv.gz extension, are not actually gzipped, they are stored plain text! (so, on many operating systems, double clicking on them will fail, because your OS will try to gunzip it and of course it can't work.
    • In site backups, they are indeed gzipped, and they are in a data folder. WarningWhat's more, the body field in the bodycontent.csv.gz file is compressed with zstd and then base64-encoded.
  2. About the attachment folder:
    • In space backups, the attachments folder is structured like in Confluence XML backups: an attachment is found at attachments/<stable page id>/<stable attachment id>/<version number>
    • In site backups, an attachment is found at attachments/<uuid> where uuid is given by the FILESTORE_ID property (provided in contentproperties.csv.gz) of the attachment revision record (defined in content.csv.gz
  3. About the whiteboards folder: only found in site exports. We haven't looked into the whiteboards yet.
  4. About the metadata.json file: only found in site exports. It's the somewhat spiritual replacement of the exportDescriptor.properties file found in XML backups. It provides a list of the .csv.gz files in the archive with their sizes in bytes, and some stuff like the backup id, account id, the cloud instance id, things probably mostly relevant to Atlassian's setup. Notably absent from this file would be a backup date. We don't currently use this file.

Description of the CSV tables

BasenameColumnsDescriptionNotes / Handling
AO_950DC3_TC_CUSTOM_EV_TYPESBELONG_SUB_CALENDAR_ID, CREATED, ICON, ID, TITLE? - related to calendarsignored
AO_950DC3_TC_DISABLE_EV_TYPESEVENT_KEY, ID, SUB_CALENDAR_ID? - related to calendarsignored
AO_950DC3_TC_EVENTSALL_DAY, CREATED, DESCRIPTION, END, ID, LAST_MODIFIED, LOCATION, ORGANISER, RECURRENCE_ID_TIMESTAMP, RECURRENCE_RULE, REMINDER_SETTING_ID, SEQUENCE, START, SUB_CALENDAR_ID, SUMMARY, URL, UTC_END, UTC_START, VEVENT_UIDCalendarignored
AO_950DC3_TC_EVENTS_EXCLALL_DAY, EVENT_ID, EXCLUSION, ID? - related to calendarsignored
AO_950DC3_TC_EVENTS_INVITEESEVENT_ID, ID, INVITEE_ID? - related to calendarsignored
AO_950DC3_TC_JIRA_REMI_EVENTSALL_DAY, ASSIGNEE, DESCRIPTION, EVENT_TYPE, ID, ISSUE_ICON_URL, ISSUE_LINK, JQL, KEY_ID, STATUS, SUB_CALENDAR_ID, TICKET_ID, TITLE, USER_ID, UTC_END, UTC_START? - related to Jira eventsignored
AO_950DC3_TC_REMINDER_SETTINGSCUSTOM_EVENT_TYPE_ID, ID, LAST_MODIFIER, PERIOD, STORE_KEY, SUB_CALENDAR_ID? - related to reminder settingsignored
AO_950DC3_TC_REMINDER_USERSID, SUB_CALENDAR_ID, USER_KEY? - related to remindersignored
AO_950DC3_TC_SUBCALSCOLOUR, CREATED, CREATOR, DESCRIPTION, ID, NAME, PARENT_ID, SPACE_KEY, STORE_KEY, SUBSCRIPTION_ID, TIME_ZONE_ID, USING_CUSTOM_EVENT_TYPE_ID, LAST_MODIFIED? - Related to calendar subscriptions?ignored
AO_950DC3_TC_SUBCALS_IN_SPACEID, SPACE_KEY, SUB_CALENDAR_ID? - Related to space calendars?ignored
AO_950DC3_TC_SUBCALS_PRIV_GRPGROUP_NAME, ID, SUB_CALENDAR_ID, TYPE, GROUP_ID? - Related to group calendars?ignored
AO_950DC3_TC_SUBCALS_PRIV_USRID, SUB_CALENDAR_ID, TYPE, USER_KEY? - Related to user calendars?ignored
AO_950DC3_TC_SUBCALS_PROPSID, KEY, SUB_CALENDAR_ID, VALUE? - Related to calendar property?ignored
audit_affected_objectid, name, type, auditrecordid?ignored
audit_changed_valueid, name, oldvalue, newvalue, hidden_oldvalue, hidden_newvalue, auditrecordid?ignored
auditrecordauditrecordid, summary, description, category, address, sysamdin, superadmin, authorname, authorfullname, authorkey, objectname, objecttype, searchstring, creationdate, eventtype, spaceid?ignored
bandanabandanaid, bandanacontext, bandanakey, bandanavalue?ignored
bodycontentbodycontentid, body, contentid, bodytypeidContains the body contents of page revisions

fully imported

NOTE: in site backups, the body field is compressed with zstd than then base64-encoded

contentcontentid, hibernateversion, contenttype, title, lowertitle, version, creator, creationdate, lastmodifier, lastmoddate, versioncomment, prevver, content_status, pageid, spaceid, child_position, parentid, messageid, pluginkey, pluginver, parentccid, draftpageid, draftspacekey, drafttype, draftpageversion, parentcommentid, username, navigationtype
content_data_classification_mappingid, classification_level_id, content_id, content_status, creation_date, last_modified_date, classification_source, classification_version_id?ignored
content_labelid, labelid, contentid, pagetemplateid, owner, creationdate, lastmoddate, labelableid, labelabletypeMaps labels to contents or templates fully imported
content_permid, cp_type, username, groupname, external_group_id, cps_id, creator, creationdate, lastmodifier, lastmoddateContent permissionsfully imported
content_perm_setid, cont_perm_type, content_id, creationdate, lastmoddateContent permission setsfully imported
content_relationrelationid, targetcontentid, sourcecontentid, targettype, sourcetype, relationname, creationdate, lastmoddate, creator, lastmodifierDocuments links between pages and wether a page is a copy of another, and possibly other relationsignored
contentpropertiespropertyid, propertyname, stringval, longval, dateval, contentidContent propertiesfully imported
default_spacerole_assignmentsprincipalid, principaltype, roleid, creator, creationdate, lastmodifier, lastmodificationdate, role_id, deleted_id
?ignored
extrnlnkslinkid, contenttype, viewcount, url, lowerurl, contentid, creator, creationdate, lastmodifier, lastmoddateExternal linksignored
follow_connectionsconnectionid, follower, followeeFollower/Followee relationshipignored
labellabelid, name, owner, namespace, creationdate, lastmoddateLabelsfully imported
likesid, contentid, username, creationdate, sequencenum, likedLiked contents per userignored
linkslinkid, destpagetitle, lowerdestpagetitle, destspacekey, lowerdestspacekey, contentid, creator, creationdate, lastmodifier, lastmoddateLinks between pagesignored
logininfoid, curfailed, totalfailed, successdate, prevsuccessdate, faileddate, username?ignored
notificationsnotificationid, contentid, labelid, spaceid, username, creator, creationdate, lastmodifier, lastmoddate, digest, network, contenttype, includechildrenNotificationsignored
os_propertyentryentity_name, entity_id, entity_key, key_type, boolean_val, double_val, string_val, text_val, long_val, int_val, date_val?ignored
pagetemplatestemplateid, hibernateversion, templatename, templatedesc, pluginkey, modulekey, refpluginkey, refmodulekey, editorversion, content, spaceid, prevver, version, creator, creationdate, lastmodifier, lastmoddate, bodytypeidTemplatesfully imported
space_aliasid, spaceid, lowerspacealias, active, lastmodifieraaid, creatoraaid, lastmodificationdate, creationdate, spacealias?ignored
space_ownerid, spaceid, ownerid, ownertypeSpace ownersignored
spacepermissionspermid, spaceid, permtype, permgroupname, externalgroupid, permusername, permalluserssubject, creator, creationdate, lastmodifier, lastmoddate, active
Space permissionsfully imported
spacerole_assignmentsrow_id, principal_id, principal_type, role_id, space_id, creator, creationdate, lastmodifier, lastmodificationdate, deleted_id?ignored
spacerole_permissionsrow_id, role_id, permission_id, creator, creationdate, lastmodifier, lastmodificationdate, deleted_id?ignored
spacerolesrole_id, spacerole_type, name, translation_tag, description, internal_use_only, creator, creationdate, lastmodifier, lastmodificationdate, deleted_id?ignored
spacesspaceid, spacename, spacekey, lowerspacekey, spacedescid, homepage, creator, creationdate, lastmodifier, lastmoddate, spacetype, spacestatusSpacesfully imported
templateattachmentpropertiespropertyid, name, value, attachmentid?ignored
templateattachmentsattachmentid, hibernateversion, name, version, prevver, templateid, creator, creationdate, lastmodifier, lastmoddate?ignored
templatepropertiespropertyid, hibernateversion, name, value, version, prevver, templateid, creator, creationdate, lastmodifier, lastmoddate?ignored
trackbacklinkslinkid, contenttype, viewcount, url, lowerurl, title, blogname, excerpt, contentid, creator, creationdate, lastmodifier, lastmoddate?ignored
user_mappinguser_key, username, lower_username, aaidMaps userkey to usernamefully imported
usercontent_relationrelationid, targetcontentid, sourceuser, targettype, relationname, creationdate, lastmoddate, creator, lastmodifierLinks contents to user, defining the kind of relationship between the two.ignored
whiteboardContentIdscontentId, spaceId, contentType, referenceIdSeems to define some contents as "whiteboards"ignored

Effectively Working with CSV backups

Unfortunately, we don't have much experience in this yet. Compared to Confluence XML, there's no huge file with everything inside it unordered that can be quite challenging to work with. However, you need to deal with gzipped (or not!) CSV files, and with the body field in the bodycontent table that can base64 encoded zstd compressed stuff.

Here are some tips anyway.

Unzip the relevant files

For instance, get the bodycontent table:

From a space export (note how we drop the annoying and wrong .gz file):

unzip -l csv-export.zip bodycontent.csv.gz > bodycontent.csv

From a site export, add a call to gunzip:

unzip -l csv-export.zip bodycontent.csv.gz | gunzip > bodycontent-with-body-still-encoded.csv

To get the body column of a site export, ignore the first line (CSV header), then take the second field, base-64 decode and then decompress the resulting zstd content:

tail -n +2 bodycontent-with-body-still-encoded.csv | cut -d, -f2 | base64 -d | zstd -d > bodies.txt

Decode a unique body value:

echo "KLUv/SAMYQAAPHA+aGVsbG88L3A+" | base64 -d | zstd -d                                                                                                  <p>hello</p>

Re-encode a body value:

echo '<p>hello</p>' | zstd | base64                                                                                                                        KLUv/QRYaQAAPHA+aGVsbG88L3A+CkTGPNg=
Information

TODO:

  • give a convenient way to rebuild a full bodycontent.csv file but with the body field decoded.
  • give a convenient way to rebuild a full bodycontent.csv file but with the body field encoded. 
Information

You can totally mess with a site backup's bodycontent table and re-import a tempered CSV file that doesn't have the body field encoded if you want to import it using our Confluence migrator. You don't need to gzip it again as well. It doesn't even need to have the .gz extension, site or space backup regardless. Everything is automatically detected. We do this in case Atlassian developers evolve their backups formats towards a more rational state, or otherwise make their site and space backups converge, which could make their own lives simpler.

Use SQLite to work with the CSV tables

SQLite can import and export CSV files.

Let's run sqlite3 in a folder where we have extracted a CSV backup:

$ sqlite3
SQLite version 3.53.2 2026-06-03 19:12:13
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.

We can import one or several CSV files, and they are immediately queriable, together:

sqlite> .import content.csv content
sqlite> .import bodycontent.csv bodycontent

For instance, let's imagine we have filtered contents using some criterion with something like this:

sqlite> delete from content where ...;

We only want to keep the body contents of the remaining content records:

sqlite> delete from bodycontent where contentid not in (select contentid from content);

Now, let's update bodycontent.csv. We set the output mode of SQLite to csv, and disable a limit that would silently truncate long fields, which can happen with body contents. We want headers, and we want to output to bodycontent.csv. We select everything, which will output in the CSV file. And then, we revert to stdout tabs output.

sqlite> .mode csv --charlimit 0
sqlite> .headers on
sqlite> .output bodycontent.csv
sqlite> select * from bodycontent;
sqlite> .output stdout
sqlite> .mode tabs

Get Connected