Migratly migrates gdocs 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 42 field mappings, of which 10 carry across intact and 32 are flagged as lossy, partial, dropped, or needing a route other than the obvious one. 3 of them need a decision from you. The gdocs side is read-only throughout — Migratly never writes to or deletes from your source.
How the structure changes
- A selected Drive folder becomes a Confluence space. My Drive's top level is offered as one too, because most personal documents sit loose in it rather than in a folder.
- Each Google Doc becomes a page in that space. Only native Google Docs are listed — uploaded .docx and PDF files have no Docs-API body and would arrive empty.
- The document body is converted to Confluence storage-format XHTML by the connector spec, not field by field.
- Structure that Google keeps only as formatting is recovered: list nesting from a per-paragraph depth number, code blocks from runs of monospaced paragraphs, block quotes from indentation, checklists from the bullet glyph.
- Drive comments become footer comments. Google's anchors are revision-scoped, so a comment arrives unanchored with the text it pointed at quoted into it.
- Links from one document to another are queued and resolved in a second pass, once both pages exist in the target.
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.
| gdocs | Confluence | Wave | How it is handled | Fields |
|---|---|---|---|---|
| Folder | Space | 0 | the collection that everything else lands in | 5 |
| User | User | 0 | accounts matched before anything is written | 3 |
| Document | Page | 1 | the records themselves | 11 |
| DocumentBody | InlineXHTML | 1 | comments, attachments and body content | 11 |
| Comment | FooterComment | 2 | comments, attachments and body content | 7 |
| DocumentLink | InlinePageLink | 3 | links between records, resolved last | 3 |
| Permission | — | 3 | skip | 1 |
| Revision | — | 3 | skip | 1 |
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.
Folder → Space
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
name |
name |
Full | |
id |
key |
Workaround | Confluence space keys must be uppercase alphanumeric and unique site-wide. Derived from the Drive folder id: strip non-alphanumerics, uppercase, take the first 8 characters, prefix GD. |
webViewLink |
description |
Partial | Drive folders have no description field. The space description is generated provenance text pointing back at the source folder. |
parents |
no equivalent | Dropped | Every selected folder becomes a TOP-LEVEL space, so nesting between two selected folders is lost. Confluence spaces cannot contain spaces. Sub-folder contents migrate only when the sub-folder is itself selected — see migration_guide.md. |
owners |
no equivalent | Dropped | Space ownership follows the API token, not the source folder owner. Confluence cannot assign a space to another user at create time. |
User → User
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
emailAddress |
email |
Full | Email is the join key AND the source-side user id — Google has no opaque user identifier to reconcile, unlike Notion. Build the email → Confluence accountId table before any page is written. |
displayName |
displayName |
Full | |
permissionId |
no equivalent | Dropped | Drive's opaque per-user id appears only inside permissions objects and has no meaning outside Google. |
Document → Page
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
name |
title |
Full | |
id |
_page_property:Google Doc ID |
Full | Keep it: this is what makes a re-run idempotent and what a later delta sync joins on. |
webViewLink |
_page_property:Source URL |
Full | |
_body |
body.value |
Partial | The Docs structural model carries formatting Confluence storage XHTML has no node for, and structure Google expresses only as formatting. See gdocs/weaknesses.md for the full ledger. |
owners.0.emailAddress |
_page_property:Original Author |
Workaround | Confluence records the API token's owner as the author of every created page and cannot impersonate. The original owner survives only as a page property. |
lastModifyingUser.emailAddress |
_page_property:Last Edited By |
Workaround | Same impersonation limit as author. |
createdTime |
_page_property:Original Created |
Workaround | Confluence sets createdAt server-side. The Google timestamp is preserved as a page property so provenance survives. |
modifiedTime |
_page_property:Original Modified |
Workaround | |
parents |
spaceId |
Full | |
starred |
no equivalent | Dropped | Starring is per-user Drive state, not document data. Confluence favourites are equally per-user and cannot be set for someone else. |
description |
_page_property:Drive Description |
Full | Drive file description — almost always empty, and unrelated to document content. |
DocumentBody → InlineXHTML
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
body.content |
body.value |
Partial | StructuralElement → storage XHTML. paragraph → <p>/<h1..6>, table → <table>, tableOfContents → toc macro, sectionBreak → dropped. |
tabs |
body.value |
Partial | Confluence pages have no tabs. Tabs are concatenated in reading order with an <h1> per tab; childTabs flatten into the same page. A document with many tabs becomes one very long page. |
lists |
body.value |
Full | glyphType decides <ol> vs <ul>; nestingLevel rebuilds containment; a ballot-box glyphSymbol becomes an <ac:task-list>. |
inlineObjects |
body.value |
Partial | imageProperties.contentUri expires in ~30 minutes and needs Google credentials, so an <ri:url> reference works during the migration and breaks afterwards. sourceUri is preferred when present. A durable result needs a rehost pass (download with source auth, upload as a Confluence attachment, rewrite to <ri:attachment>) — not yet implemented for this pair. Asks you first |
footnotes |
body.value |
Partial | Confluence storage format has no footnote node. Referenced footnotes are collected into a 'Footnotes' section appended to the page and linked by anchor; numbering follows reference order, which diverges from Google's when a footnote is referenced twice. |
positionedObjects |
no equivalent | Dropped | Floating (text-wrapped) images are anchored to a paragraph rather than sitting in its element list, so a body walk never encounters them. Inline images are unaffected. |
headers |
no equivalent | Dropped | Running page headers are a print concept with no wiki equivalent. |
footers |
no equivalent | Dropped | Running page footers are a print concept with no wiki equivalent. |
documentStyle |
no equivalent | Dropped | Page size, margins and column layout do not apply to a wiki page. |
namedRanges |
no equivalent | Dropped | Add-on defined text ranges. Confluence has no positional-range concept to hold them. |
suggestionsViewMode |
no equivalent | Dropped | Reads fold pending suggestions in as though accepted. Unaccepted edits therefore ship as approved text. Warn the user to resolve suggestions BEFORE the run. Asks you first |
Comment → FooterComment
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
content |
body.value |
Partial | Google comments are plain text; Confluence footer comments are storage XHTML. Text converts cleanly, but the comment lands unanchored. |
author.displayName |
no equivalent | Workaround | The API token's owner is recorded as the author of every migrated comment. The original name survives only in the comment text prefix. |
createdTime |
no equivalent | Workaround | Server-set on the target. Preserved in the comment text prefix. |
quotedFileContent.value |
no equivalent | Workaround | The text the comment was anchored to. Since the anchor itself cannot be reproduced, quoting this into the comment body is the only way a reader can tell what was being discussed. |
replies |
no equivalent | Partial | Confluence footer comments support threading, but the executor writes each reply as its own top-level footer comment with an author prefix. Thread shape flattens. |
anchor |
no equivalent | Dropped | Opaque, revision-scoped region reference. Meaningless against a different document in a different system. |
resolved |
no equivalent | Dropped | Confluence's v2 footer-comment create takes no resolved flag. A resolved Google comment migrates as an open one; prefix it with '[Resolved]' if that distinction matters. |
DocumentLink → InlinePageLink
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
_doc_to_doc_link |
body.value |
Full | A link to https://docs.google.com/document/d/<ID>/… is an intra-source reference, not an external URL. The converter routes both textRun links and richLink elements through the target's ref_resolution policy: inline when the referenced page already exists, otherwise a placeholder that the resolution wave swaps for <ac:link><ri:page> once it does. |
link.headingId |
no equivalent | Dropped | In-document anchors point at an opaque Google heading id. Confluence derives its own anchors from heading TEXT, so the id cannot be translated — the link text survives, the jump does not. |
link.bookmarkId |
no equivalent | Dropped | Same as headingId — an opaque Google identifier with no target equivalent. |
Permission →
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
* |
no equivalent | Pending | Drive grants access per file; Confluence grants it per space. There is no faithful translation — a per-document permission set collapses into one space-level policy. Surface the source permissions to the admin and let them set space permissions deliberately rather than guessing. Asks you first |
Revision →
| gdocs field | Confluence field | Coverage | Notes |
|---|---|---|---|
* |
no equivalent | Dropped | Google exposes only pinned revisions and recent autosaves, and no wiki target can accept another system's edit history. Migrated pages start at version 1 with the migration as their only author. |
What does not carry across cleanly
These are the 30 mappings where Confluence cannot hold what gdocs holds. Each one appears in your coverage report with the same reason, and with the alternative routes available.
permissionIdDropped
Drive's opaque per-user id appears only inside permissions objects and has no meaning outside Google.idWorkaround
Confluence space keys must be uppercase alphanumeric and unique site-wide. Derived from the Drive folder id: strip non-alphanumerics, uppercase, take the first 8 characters, prefix GD.webViewLinkPartial
Drive folders have no description field. The space description is generated provenance text pointing back at the source folder.parentsDropped
Every selected folder becomes a TOP-LEVEL space, so nesting between two selected folders is lost. Confluence spaces cannot contain spaces. Sub-folder contents migrate only when the sub-folder is itself selected — see migration_guide.md.ownersDropped
Space ownership follows the API token, not the source folder owner. Confluence cannot assign a space to another user at create time._bodyPartial
The Docs structural model carries formatting Confluence storage XHTML has no node for, and structure Google expresses only as formatting. See gdocs/weaknesses.md for the full ledger.owners.0.emailAddressWorkaround
Confluence records the API token's owner as the author of every created page and cannot impersonate. The original owner survives only as a page property.lastModifyingUser.emailAddressWorkaround
Same impersonation limit as author.createdTimeWorkaround
Confluence sets createdAt server-side. The Google timestamp is preserved as a page property so provenance survives.starredDropped
Starring is per-user Drive state, not document data. Confluence favourites are equally per-user and cannot be set for someone else.body.contentPartial
StructuralElement → storage XHTML. paragraph → <p>/<h1..6>, table → <table>, tableOfContents → toc macro, sectionBreak → dropped.tabsPartial
Confluence pages have no tabs. Tabs are concatenated in reading order with an <h1> per tab; childTabs flatten into the same page. A document with many tabs becomes one very long page.inlineObjectsPartial
imageProperties.contentUri expires in ~30 minutes and needs Google credentials, so an <ri:url> reference works during the migration and breaks afterwards. sourceUri is preferred when present. A durable result needs a rehost pass (download with source auth, upload as a Confluence attachment, rewrite to <ri:attachment>) — not yet implemented for this pair.footnotesPartial
Confluence storage format has no footnote node. Referenced footnotes are collected into a 'Footnotes' section appended to the page and linked by anchor; numbering follows reference order, which diverges from Google's when a footnote is referenced twice.positionedObjectsDropped
Floating (text-wrapped) images are anchored to a paragraph rather than sitting in its element list, so a body walk never encounters them. Inline images are unaffected.headersDropped
Running page headers are a print concept with no wiki equivalent.footersDropped
Running page footers are a print concept with no wiki equivalent.documentStyleDropped
Page size, margins and column layout do not apply to a wiki page.namedRangesDropped
Add-on defined text ranges. Confluence has no positional-range concept to hold them.suggestionsViewModeDropped
Reads fold pending suggestions in as though accepted. Unaccepted edits therefore ship as approved text. Warn the user to resolve suggestions BEFORE the run.contentPartial
Google comments are plain text; Confluence footer comments are storage XHTML. Text converts cleanly, but the comment lands unanchored.author.displayNameWorkaround
The API token's owner is recorded as the author of every migrated comment. The original name survives only in the comment text prefix.createdTimeWorkaround
Server-set on the target. Preserved in the comment text prefix.quotedFileContent.valueWorkaround
The text the comment was anchored to. Since the anchor itself cannot be reproduced, quoting this into the comment body is the only way a reader can tell what was being discussed.repliesPartial
Confluence footer comments support threading, but the executor writes each reply as its own top-level footer comment with an author prefix. Thread shape flattens.anchorDropped
Opaque, revision-scoped region reference. Meaningless against a different document in a different system.resolvedDropped
Confluence's v2 footer-comment create takes no resolved flag. A resolved Google comment migrates as an open one; prefix it with '[Resolved]' if that distinction matters.link.headingIdDropped
In-document anchors point at an opaque Google heading id. Confluence derives its own anchors from heading TEXT, so the id cannot be translated — the link text survives, the jump does not.link.bookmarkIdDropped
Same as headingId — an opaque Google identifier with no target equivalent.*Dropped
Google exposes only pinned revisions and recent autosaves, and no wiki target can accept another system's edit history. Migrated pages start at version 1 with the migration as their only author.
Decisions you will be asked to confirm
Migratly stops and asks rather than guessing when the right answer depends on your instance.
inlineObjects→body.value
imageProperties.contentUri expires in ~30 minutes and needs Google credentials, so an <ri:url> reference works during the migration and breaks afterwards. sourceUri is preferred when present. A durable result needs a rehost pass (download with source auth, upload as a Confluence attachment, rewrite to <ri:attachment>) — not yet implemented for this pair.suggestionsViewMode→ unmapped
Reads fold pending suggestions in as though accepted. Unaccepted edits therefore ship as approved text. Warn the user to resolve suggestions BEFORE the run.*→ unmapped
Drive grants access per file; Confluence grants it per space. There is no faithful translation — a per-document permission set collapses into one space-level policy. Surface the source permissions to the admin and let them set space permissions deliberately rather than guessing.
How a gdocs to Confluence migration runs
- Connect both systems. gdocs 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 gdocs instance.
- Coverage report. All 42 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 gdocs to Confluence?
Yes. gdocs to Confluence is a supported pair with a mapping covering 8 entity types and 42 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 gdocs to Confluence?
32 of the 42 field mappings for this pair are flagged rather than clean. For example, `permissionId` is marked dropped: Drive's opaque per-user id appears only inside permissions objects and has no meaning outside Google. 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 gdocs 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 gdocs data modified during the migration?
No. The source is read-only for the entire run. Migratly never writes to, archives, or deletes anything in gdocs, and every action taken against Confluence is written to an append-only audit log.
Does a Google Docs to Confluence migration keep tables and formatting?
Headings, lists, tables, links, footnotes and inline styling all carry across. Merged cells, column widths and cell shading do not, and a header row survives only when Google actually marked it as one — many documents just bold the first row, which arrives as ordinary bold text.
What happens to images in a Google Doc?
Google serves inline images from a URL that expires in about 30 minutes and needs the original credentials. Migratly reports every image before the run rather than letting them silently break afterwards; a durable result needs the image re-hosted as a Confluence attachment, which is flagged as follow-up work rather than claimed as done.
Are tracked changes and suggestions migrated?
No. The Docs API returns a document with pending suggestions folded in as though they were accepted, and discards the suggestion metadata. Resolve or reject suggestions before the run, or unapproved edits ship as approved text.
Can Migratly migrate Word documents stored in Google Drive?
Not through this pair. An uploaded .docx is a different file type with no Docs-API body, so it is excluded deliberately rather than migrated as an empty page. Moving those files is the Google Drive connector's job.
How long does a Google Docs migration take?
The Google Docs API allows 60 reads per minute per user and every page costs exactly one read, so throughput tops out near one document per second regardless of parallelism. A few thousand documents is over an hour of pure API time, which Migratly estimates before the run starts.