Migratly migrates Notion to Confluence by reading both APIs, deriving the mapping between them, and showing you what it found before anything is written. For this pair that is 8 entity types and 53 field mappings, of which 24 carry across intact and 28 are flagged as lossy, partial, dropped, or needing a route other than the obvious one. One of them needs a decision from you. The Notion side is read-only throughout — Migratly never writes to or deletes from your source.
How the structure changes
- A Notion workspace or top-level page becomes a Confluence space.
- A Notion database becomes a parent page whose children are its rows — Confluence has no database or row concept.
- Notion blocks are converted to Confluence storage-format XHTML by the connector spec, not field by field.
- Inline page mentions,
link_to_pageblocks and child pages are queued and resolved in a second pass that handles cycles, forward references and orphaned sub-pages. - Pages with no content still arrive, carrying a spec-declared panel that links back to the Notion original.
What maps to what
Entities are written in dependency waves. Everything at wave 0 exists before anything at wave 1 is created, which is what makes cross-references resolvable and the whole run resumable from a cursor.
| Notion | Confluence | Wave | How it is handled | Fields |
|---|---|---|---|---|
| Database | Space | 0 | the collection that everything else lands in | 5 |
| User | User | 0 | accounts matched before anything is written | 4 |
| Block | InlineXHTML | 1 | comments, attachments and body content | 3 |
| ChildPageBlock | Page | 1 | the records themselves | 2 |
| Page | Page | 1 | the records themselves | 28 |
| Attachment | Attachment | 2 | comments, attachments and body content | 4 |
| Comment | FooterComment | 2 | comments, attachments and body content | 5 |
| Relation | InlinePageLink | 3 | links between records, resolved last | 2 |
Field-by-field coverage
Every mapping carries one of five labels. Full means it carries across intact. Partial means the value survives with a narrowed range. Lossy means something is lost. Workaround means there is no native slot and it is carried another way. Dropped means the target will not accept it. Anything short of full is shown to you before the run, not discovered afterwards.
Database → Space
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
title |
name |
Full | Notion database titles are rich_text arrays — flatten to plain text for the space name. |
id |
key |
Workaround | Confluence space keys are uppercase alphanumeric. Generated from the Notion database id (first 8 hex chars, prefixed with NTN). |
description |
description |
Partial | Notion description is rich_text — converted to a single plain paragraph for the space description. |
icon |
no equivalent | Dropped | Confluence space icons are uploaded separately via /space/{key}/setting and are out of scope for v1. |
properties |
no equivalent | Workaround | Notion database property schema is rendered as a Page Properties summary on each child page (per row). The schema itself is preserved as a page property on the database-parent page. |
User → User
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
id |
Original Notion User ID |
Lossy | Confluence users are identified by accountId. Notion user IDs are preserved for audit only. |
name |
displayName |
Full | |
person.email |
email |
Full | Email is the join key. Build the Notion userId -> Confluence accountId table before any pages migrate. |
type |
no equivalent | Dropped | Notion bot users have no Confluence equivalent and are filtered out. |
Block → InlineXHTML
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
type |
_xhtml_tag |
Partial | Most Notion block types map to a clean storage XHTML element. Edge cases: synced_block flattens, button drops, equation requires Math macro app. |
{block_type}.rich_text |
_xhtml_inline |
Partial | Annotations bold/italic/strike/code/link map directly. Color annotations preserved via inline style. User mentions require user_mapping_table. |
{block_type}.children |
_xhtml_nested |
Full | Nested blocks recurse into nested XHTML containers (e.g. nested <ul> inside <li>). |
ChildPageBlock → Page
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
child_page.title |
title |
Full | |
_parent_block.page_id |
parentId |
Full |
Page → Page
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
id |
_page_property:Notion Page ID |
Full | |
properties.title |
title |
Full | |
parent.database_id |
parentId |
Full | |
parent.page_id |
parentId |
Full | |
_blocks |
body.value |
Partial | Notion's block model is richer than Confluence storage XHTML. Most blocks convert directly; formula/rollup compute to text; synced_blocks flatten; buttons drop. |
_comments |
no equivalent | Workaround | Notion comments are per-block. Confluence's API only supports page-level (footer) comments. Each Notion comment becomes one footer comment with a leading author/date line. |
properties.{select} |
_page_property + label |
Full | |
properties.{multi_select} |
_labels |
Full | |
properties.{status} |
_page_property:Status + label |
Full | Asks you first |
properties.{date} |
_page_property |
Full | |
properties.{number} |
_page_property |
Full | |
properties.{checkbox} |
_page_property |
Full | |
properties.{url} |
_page_property + body_link |
Full | |
properties.{email} |
_page_property |
Full | |
properties.{phone_number} |
_page_property |
Full | |
properties.{people} |
_page_property + body_mention |
Partial | Confluence mentions require accountId. Notion users without a matching Confluence email fall back to plain text 'Originally assigned to [name]'. |
properties.{files} |
_attachments |
Partial | Notion-hosted files have signed URLs that expire in 1 hour — must be downloaded and re-uploaded to Confluence during the same run. External URLs are kept as link page properties. |
properties.{relation} |
body.value |
Partial | Confluence has no native relation type. Each related Notion page id is rendered as an inline <ri:page> link inside the page body in a 'Related' section. Requires all pages created first. |
properties.{rollup} |
_page_property |
Workaround | Notion rollup logic is not reproducible in Confluence. The computed value at migration time is captured as a frozen page property. |
properties.{formula} |
_page_property |
Workaround | Notion formula logic is not reproducible in Confluence. The computed value at migration time is captured as a frozen page property. |
created_time |
_page_property:Original Created |
Workaround | Confluence createdAt is server-set. Original timestamp preserved as page property. |
last_edited_time |
_page_property:Original Updated |
Workaround | Confluence updatedAt is server-set on every PUT. Stored as page property for audit. |
created_by.id |
_page_property:Original Author |
Workaround | Confluence cannot set page author. Original Notion author preserved as page property; if a Confluence mapping exists, also rendered as an info-panel at the top of the body. |
last_edited_by.id |
no equivalent | Dropped | Already covered by created_by; tracking the last editor per-page in Confluence is automatic going forward. |
icon |
no equivalent | Dropped | Confluence pages have no native emoji/icon field via API. Could be rendered as a heading prefix — opt-in only. |
cover |
no equivalent | Dropped | Confluence pages have no cover image field. |
archived |
status |
Full | |
url |
_page_property:Original Notion URL |
Full |
Attachment → Attachment
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
file.url |
no equivalent | Partial | Notion-hosted: download bytes, then multipart upload to Confluence. External-URL files: skip rehost, render as inline link in the page body. |
name |
filename |
Full | |
_size |
no equivalent | Internal | Used to skip files >100MB (Confluence default per-file limit). |
caption |
_xhtml_inline |
Full | Notion file caption rendered as the inline link/figure caption in the page body next to the attachment reference. |
Comment → FooterComment
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
rich_text |
body.value |
Partial | Notion comments are anchored to a specific block. Confluence footer comments are anchored to the whole page — the block context is lost (unless inline-comment anchors are added in a second pass, which is out of scope for v1). |
created_by.id |
no equivalent | Workaround | Confluence cannot author comments as another user. Original commenter name is prepended to the body. |
created_time |
no equivalent | Workaround | Confluence comment createdAt is server-set. Original timestamp inlined into the prepended header. |
discussion_id |
no equivalent | Dropped | Notion comment threads (discussion_id) have no Confluence equivalent on footer comments. Replies flatten to siblings under the same page. |
parent.block_id |
pageId |
Partial | Notion comments anchor to a block; we re-target the comment to the block's page. The exact in-page location is not preserved. |
Relation → InlinePageLink
| Notion field | Confluence field | Coverage | Notes |
|---|---|---|---|
relation[].id |
body.value._related_section |
Partial | Confluence has no relation type. Each related Notion page id resolves to a Confluence page; inline links are appended under a 'Related' heading. No back-relation is created. |
_property_name |
body.value._related_section.heading |
Full | Notion lets multiple relation properties exist per page (e.g. 'Blocked by', 'Related to'). Each becomes its own subheading in the Related section so the original semantics are preserved. |
What does not carry across cleanly
These are the 28 mappings where Confluence cannot hold what Notion holds. Each one appears in your coverage report with the same reason, and with the alternative routes available.
idLossy
Confluence users are identified by accountId. Notion user IDs are preserved for audit only.typeDropped
Notion bot users have no Confluence equivalent and are filtered out.idWorkaround
Confluence space keys are uppercase alphanumeric. Generated from the Notion database id (first 8 hex chars, prefixed with NTN).descriptionPartial
Notion description is rich_text — converted to a single plain paragraph for the space description.iconDropped
Confluence space icons are uploaded separately via /space/{key}/setting and are out of scope for v1.propertiesWorkaround
Notion database property schema is rendered as a Page Properties summary on each child page (per row). The schema itself is preserved as a page property on the database-parent page._blocksPartial
Notion's block model is richer than Confluence storage XHTML. Most blocks convert directly; formula/rollup compute to text; synced_blocks flatten; buttons drop._commentsWorkaround
Notion comments are per-block. Confluence's API only supports page-level (footer) comments. Each Notion comment becomes one footer comment with a leading author/date line.properties.{people}Partial
Confluence mentions require accountId. Notion users without a matching Confluence email fall back to plain text 'Originally assigned to [name]'.properties.{files}Partial
Notion-hosted files have signed URLs that expire in 1 hour — must be downloaded and re-uploaded to Confluence during the same run. External URLs are kept as link page properties.properties.{relation}Partial
Confluence has no native relation type. Each related Notion page id is rendered as an inline <ri:page> link inside the page body in a 'Related' section. Requires all pages created first.properties.{rollup}Workaround
Notion rollup logic is not reproducible in Confluence. The computed value at migration time is captured as a frozen page property.properties.{formula}Workaround
Notion formula logic is not reproducible in Confluence. The computed value at migration time is captured as a frozen page property.created_timeWorkaround
Confluence createdAt is server-set. Original timestamp preserved as page property.last_edited_timeWorkaround
Confluence updatedAt is server-set on every PUT. Stored as page property for audit.created_by.idWorkaround
Confluence cannot set page author. Original Notion author preserved as page property; if a Confluence mapping exists, also rendered as an info-panel at the top of the body.last_edited_by.idDropped
Already covered by created_by; tracking the last editor per-page in Confluence is automatic going forward.iconDropped
Confluence pages have no native emoji/icon field via API. Could be rendered as a heading prefix — opt-in only.coverDropped
Confluence pages have no cover image field.typePartial
Most Notion block types map to a clean storage XHTML element. Edge cases: synced_block flattens, button drops, equation requires Math macro app.{block_type}.rich_textPartial
Annotations bold/italic/strike/code/link map directly. Color annotations preserved via inline style. User mentions require user_mapping_table.rich_textPartial
Notion comments are anchored to a specific block. Confluence footer comments are anchored to the whole page — the block context is lost (unless inline-comment anchors are added in a second pass, which is out of scope for v1).created_by.idWorkaround
Confluence cannot author comments as another user. Original commenter name is prepended to the body.created_timeWorkaround
Confluence comment createdAt is server-set. Original timestamp inlined into the prepended header.discussion_idDropped
Notion comment threads (discussion_id) have no Confluence equivalent on footer comments. Replies flatten to siblings under the same page.parent.block_idPartial
Notion comments anchor to a block; we re-target the comment to the block's page. The exact in-page location is not preserved.file.urlPartial
Notion-hosted: download bytes, then multipart upload to Confluence. External-URL files: skip rehost, render as inline link in the page body.relation[].idPartial
Confluence has no relation type. Each related Notion page id resolves to a Confluence page; inline links are appended under a 'Related' heading. No back-relation is created.
Decisions you will be asked to confirm
Migratly stops and asks rather than guessing when the right answer depends on your instance.
properties.{status}→_page_property:Status + label
Confluence has no native status concept. Confirm whether status should also be rendered as a coloured banner at the top of the page body.
How a Notion to Confluence migration runs
- Connect both systems. Notion is connected read-only. Migratly never writes to, archives or deletes anything on the source side.
- Schema discovery. Both APIs are read live, including your custom fields, so the mapping reflects your instance rather than a generic Notion instance.
- Coverage report. All 53 mappings are presented with their coverage. You confirm, reroute or exclude before anything is written.
- Execution in 4 waves. Writes are idempotent and cursor-tracked, so an interrupted run resumes instead of restarting or duplicating.
- Verification. The result is checked back against the source, and every action is in an append-only audit log.
Questions
Can Migratly migrate Notion to Confluence?
Yes. Notion to Confluence is a supported pair with a mapping covering 8 entity types and 53 fields. You connect both systems, Migratly reads the live schemas on each side, and it produces a coverage report you approve before any data moves.
What is lost when migrating from Notion to Confluence?
28 of the 53 field mappings for this pair are flagged rather than clean. For example, `id` is marked lossy: Confluence users are identified by accountId. Notion user IDs are preserved for audit only. Every flagged mapping appears in the coverage report before the run, with its reason and the routes still open to it. Nothing is dropped silently.
How long does a Notion to Confluence migration take?
It is bounded by the two APIs' rate limits rather than by Migratly. This pair runs in 4 dependency waves, starting with account matching and ending with cross-references, and every wave is resumable — a run interrupted halfway continues from its cursor rather than starting over or duplicating what it already wrote.
Is my Notion data modified during the migration?
No. The source is read-only for the entire run. Migratly never writes to, archives, or deletes anything in Notion, and every action taken against Confluence is written to an append-only audit log.
Can a Notion database be migrated to Confluence?
Yes. Confluence has no database object, so the database becomes a parent page and each row becomes a child page beneath it. Database properties become page properties or labels depending on their type.
Do Notion page links still work after migrating to Confluence?
Yes. Inline mentions and page links are rewritten to Confluence ri:page references in a resolution pass that runs after every page exists, which is what makes circular and forward references resolvable.
What Notion content does not convert to Confluence?
Blocks with no Confluence equivalent — synced blocks, embedded databases and some third-party embeds — are the usual gaps. Every one is listed in the coverage report before the run, so nothing is discovered after the fact.