Document360 to Confluence Migration: The Complete Technical Guide
Document360 to Confluence migration requires converting Markdown/HTML to Confluence Storage Format, handling API limits, and rewriting every internal link.
There is no native, one-click migration path from Document360 to Confluence. Document360 exports articles as .md, .html, or .json depending on editor type and language selection. Confluence requires content in its proprietary Storage Format (an XHTML-based XML dialect with custom macro elements) or Atlassian Document Format (ADF, a JSON schema). The Confluence REST API rejects raw Markdown and standard HTML — push Document360's raw export to the API and the request will fail. (confluence.atlassian.com)
The real work in this migration is format conversion, media handling, and link rewriting. This guide covers what can and cannot be migrated, the exact data-mapping decisions you will face, the API rate limits on both sides, and the edge cases that cause silent data loss.
As of April 2026, Confluence Cloud does offer a native HTML import flow under Import from other tools. It accepts a ZIP of .html files, creates a new space, and uses filenames as page names. But it has significant limitations: it cannot process Markdown or multilingual JSON exports, code blocks degrade to plain text, iframes are unsupported, and Atlassian does not document a folder-to-page-tree convention for deep hierarchies. For Document360 workspaces with Markdown articles, mixed editor types, or heavy cross-linking, the HTML importer is only part of the job. (support.atlassian.com)
Do not confuse Confluence's two import flows. The older document importer handles Word, Google Docs, and OneDrive files in batches of up to 30 files and 50 MB total. The newer HTML importer is a separate flow that creates a new space from a ZIP of HTML files. They have different limits and different behavior. (support.atlassian.com)
| Migration Path | Fidelity | Format Conversion | Attachments | Link Rewriting | Best For |
|---|---|---|---|---|---|
| Manual copy-paste | Low | Manual | Manual re-upload | ❌ | Under 20 articles |
| Confluence HTML import | Low–Medium | Partial (HTML only) | Page-matched folders | ❌ | Small, HTML-only, low-metadata projects |
| ZIP export → Marketplace importer | Low–Medium | Partial | ❌ (inline images break) | ❌ | Text-heavy articles, minimal media |
| DIY API scripts | Medium–High | ✅ (custom parser) | ✅ (separate pipeline) | ✅ (custom) | Engineering teams with time |
| ClonePartner managed migration | Highest | ✅ (automated) | ✅ | ✅ | Complex knowledge bases, zero downtime |
For a detailed breakdown of Confluence's native import capabilities and limits, see How to Import Data into Confluence: Methods, API Limits & Mapping.
The Format Conversion Challenge: Markdown/HTML to Confluence Storage Format
This is the single biggest technical hurdle, and most teams underestimate it.
Document360's export format depends on which editor was used to create each article. Articles from the Markdown editor export as .md files. Articles from the WYSIWYG or Advanced WYSIWYG editor export as .html files. If you export a multilingual project, Document360 outputs everything as .json. A single knowledge base can contain a mix of all three. (docs.document360.com)
Confluence does not accept any of these formats natively. Atlassian's content-body conversion APIs handle conversions between storage, atlas_doc_format, and editor-related representations — not from raw Markdown or arbitrary HTML. The API requires the page body in one of two representations:
- Storage Format — XHTML-based XML with custom
<ac:>and<ri:>elements for macros, links, and embedded resources - Atlassian Document Format (ADF) — a JSON schema used by the v2 API
A simple Markdown heading like ## Getting Started becomes <h2>Getting Started</h2> in Storage Format. That is the trivial part. The hard part is everything else:
| Element | Document360 Format | Confluence Storage Format |
|---|---|---|
| Code blocks | Standard backticks (```) |
<ac:structured-macro ac:name="code"> with CDATA body |
| Tables | Pipe syntax (` | col |
| Internal links | Slugs or href URLs |
<ac:link> with <ri:page> referencing title and space key |
| Inline images | <img src="..."> or ! [alt](url) |
<ac:image><ri:attachment ri:filename="..."/></ac:image> |
| Callouts | HTML/Markdown variants | <ac:structured-macro ac:name="info"> or similar |
Here is a concrete example. A code block extracted from Document360:
```javascript
console.log("Hello World");
Must become this in Confluence Storage Format:
```xml
<ac:structured-macro ac:name="code" ac:schema-version="1">
<ac:parameter ac:name="language">javascript</ac:parameter>
<ac:plain-text-body><![CDATA[console.log("Hello World");]]></ac:plain-text-body>
</ac:structured-macro>
And an internal link:
<!-- Document360 HTML -->
<a href="/docs/getting-started">Getting Started</a>
<!-- Confluence Storage Format -->
<ac:link>
<ri:page ri:content-title="Getting Started" ri:space-key="DOCS" />
</ac:link>If your parser misses a closing tag or fails to wrap special characters in CDATA blocks, Confluence rejects the entire page payload with a 400 Bad Request or 500 Internal Server Error. Confluence Storage Format is strict XML — a single unclosed tag or unescaped ampersand causes rejection of the whole page.
Building a reliable parser that handles all of these conversions — including edge cases like nested tables, mixed Markdown/HTML content, and embedded video iframes — is the bulk of the engineering work in a DIY migration.
What Can (and Cannot) Be Migrated from Document360
What migrates
| Document360 Object | Migration Path | Notes |
|---|---|---|
| Articles (body content) | API or ZIP export | Content must be converted to Storage Format or ADF |
| Categories & subcategories | API or ZIP export | Hierarchy preserved in the ZIP's folder structure and JSON manifest |
| Media files (images, PDFs) | ZIP export includes a Media folder |
Must be re-uploaded as Confluence attachments via API |
| Article metadata (title, slug, order, status) | API only | ZIP includes order and status; API provides richer metadata |
| Article versioning | API only (per-version retrieval) | Confluence stores versions natively, but you must create them sequentially |
| Multilingual content | API or ZIP export (per-language) | Each language must be handled as a separate migration pass |
What does NOT migrate
Document360's native project export explicitly excludes content reuse elements. Templates, variables, snippets, and glossary terms are not part of the export/import process and must be recreated manually. (docs.document360.com)
| Document360 Object | Why It Doesn't Migrate |
|---|---|
| Templates | Not included in ZIP export; no API endpoint for bulk template extraction |
| Variables | Excluded from export — rendered inline when using isForDisplay=true via API, but the variable definitions themselves are lost |
| Snippets | Reusable content blocks are not exported as discrete objects |
| Glossary terms | Not included in export; no direct Confluence equivalent |
| Article analytics (views, ratings, feedback) | Not exportable; Document360 analytics are platform-specific |
| Workflow status assignments | Workflow metadata is not included in the export ZIP |
| Reader accounts & permissions | User/reader data does not transfer; Confluence uses its own permission model |
| Custom domain / SEO settings | Platform-specific configuration |
| API documentation (OpenAPI specs) | Interactive API docs are a separate Document360 module with no Confluence parallel |
If your documentation relies heavily on Document360 snippets or variables, you have a choice when extracting via API. Setting isForDisplay=true expands snippets and variables into the article body, giving you the rendered version that matches what readers see. Setting isForDisplay=false returns raw tokens that will not render in Confluence. The rendered version is usually the safer choice for migration fidelity, but you lose the ability to recreate reusable content blocks on the Confluence side. Also set appendSASToken=false so you do not accidentally migrate SAS-signed asset URLs into Confluence. (apidocs.document360.com)
WebHelp export is already lossy. Document360's HTML5 WebHelp export includes search, media support, and internal links, which makes it look like a good Confluence import source. But WebHelp excludes attachments, glossary definitions, inline comments, breadcrumbs, article-level table of contents, and previous/next navigation. If attachments or glossary content matter, WebHelp is not a full-fidelity source. (docs.document360.com)
For teams evaluating the reverse direction, see Confluence to Document360 Migration: API Limits & Data Mapping Guide.
Data Mapping: Document360 Architecture → Confluence Architecture
The architectural mismatch between Document360 and Confluence is where most planning mistakes happen. Document360 organizes content as Projects → Workspaces → Categories → Articles. Confluence uses Sites → Spaces → Pages (with parent-child hierarchy).
| Document360 Concept | Confluence Equivalent | Notes |
|---|---|---|
| Project | Confluence Site | 1:1 — each D360 project maps to a Confluence Cloud site |
| Workspace | Space | Each workspace becomes its own Confluence Space with a unique space key |
| Top-level Category | Parent Page (under Space homepage) | Categories become pages that serve as containers — add overview text or they appear blank |
| Subcategory | Child Page (nested under parent) | Confluence supports deep nesting; mirror D360's category depth |
| Article | Page (child of its category page) | Body converted to Storage Format; title preserved |
| Article order | Page order within parent | Set via the Confluence API's position parameter on v2 endpoints |
| Media file | Attachment on the page that references it | Or attach to a shared "assets" page if media is reused across articles |
| Article slug | Page title (URL auto-generated) | Confluence auto-generates URLs from titles; no custom slug support |
| Custom fields | Page properties or labels | Requires explicit mapping — will not appear automatically |
Spaces cannot be nested in Confluence, but pages can be nested to arbitrary depth. The category tree belongs in the page hierarchy, not in separate spaces.
The title collision problem
Document360 allows multiple articles to share the same name as long as they live in different categories. Confluence enforces a strict rule: all pages within a single Space must have a unique title. If you migrate two articles named "Authentication" into the same Confluence Space, the API will reject the second one.
Your migration logic must detect duplicate titles and apply a deterministic rename rule before import — for example, appending the category name ("Authentication - API" vs. "Authentication - SSO"). If you use the Confluence HTML importer, the same constraint applies: filenames become page names, and duplicates will collide. (support.atlassian.com)
Handling versioned projects
Document360 supports project versions (e.g., v1.0, v2.0) — separate documentation sets for different product releases. Confluence has no native equivalent. Your options:
- One Space per version —
DOCS-V1,DOCS-V2. Clean separation, but content duplication. - Version labels — Tag all pages with a label like
version-2.0and use Confluence's CQL search to filter. Less duplication, but harder to navigate. - Archive older versions — Migrate only the latest version as active pages. Export older versions to PDF and attach them for reference.
Multilingual content
Document360 exposes available_languages and translation status per article. Since Confluence requires unique page names within a space, repeated titles like "Overview" or "Introduction" across languages will collide. Separate spaces per language are usually cleaner than a single multilingual space with language-root pages. (apidocs.document360.com)
For teams thinking through Confluence Space architecture from other migration contexts, see SharePoint to Confluence Migration: The Complete Technical Guide.
API Rate Limits: Document360 and Confluence Constraints
Both platforms enforce rate limits that directly affect migration speed. Ignoring them does not just slow you down — it causes dropped records.
Document360 API rate limits
Rate limits are applied per api_token, and each subscription tier has different allowances (apidocs.document360.com):
- Standard: No API access
- Professional and Business: 60 requests per minute
- Enterprise, Enterprise Plus, and Trial: 100 requests per minute
Every API call (GET, POST, PUT, DELETE) counts as one request. The X-RateLimit-Remaining header is included in every response by default. X-RateLimit-Limit and X-RateLimit-Reset headers only appear once the limit is reached. When exhausted, the API returns HTTP 429 and you must wait for the reset window.
If you script the project export through the API instead of the UI, there is a separate ceiling: the export-project API endpoint is Enterprise-only and can be used twice per day. That works for scheduled production exports but is terrible for trial-and-error development. (docs.document360.com)
Before starting a migration, make a few test calls and check the X-RateLimit-Remaining header to determine your actual quota. If you are migrating hundreds of articles via API on a Professional or Business plan, you may need to batch your calls across multiple reset windows.
Confluence Cloud API rate limits
Confluence Cloud uses a points-based rate-limit model that went into phased enforcement starting March 2, 2026. Instead of counting raw requests, each API call consumes points based on operation complexity and payload size.
There is a critical distinction here that many migration guides get wrong: the points-based quotas apply to Forge, Connect, and OAuth 2.0 (3LO) apps. API token-based traffic is not subject to the new points-based quotas and remains under existing burst limits. If your migration script uses basic API tokens, you still need backoff and retry logic for 429 responses, but you do not automatically inherit the app-tier point quotas. (developer.atlassian.com)
Key constraints for migration scripts regardless of authentication method:
- Burst limits: Per-second rate limits apply per endpoint, independent of any hourly quota. Sending too many requests per second triggers HTTP 429 even if you have quota remaining.
- Body expansion cap: API queries that expand
body.storageare limited to 50 results per request. - Page body size: Content bodies exceeding roughly 64 KB can trigger HTTP 400 errors.
- 429 response handling: The
Retry-Afterheader tells you how many seconds to wait. Implement exponential backoff with jitter — do not just retry immediately.
# Rate-limit-aware loop for Confluence page creation
import time
import random
import requests
def create_page_with_backoff(url, headers, payload, max_retries=5):
for attempt in range(max_retries):
resp = requests.post(url, headers=headers, json=payload)
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 60))
jitter = random.uniform(0, retry_after * 0.1)
time.sleep(retry_after + jitter)
else:
resp.raise_for_status()
raise Exception("Max retries exceeded")If you use the native HTML importer instead of the API, your limiting factor may be Confluence's attachment size setting, not a published HTML-import cap. Atlassian's FAQ tells admins to increase the attachment-size limit so it exceeds the HTML ZIP size before import. (support.atlassian.com)
For a deeper look at Confluence API behavior during large imports, see How to Import Data into Confluence: Methods, API Limits & Mapping.
Handling Embedded Media and Rewriting Internal Links
These two operations cause the most silent data loss in Document360-to-Confluence migrations. Both require multi-step API workflows.
Embedded media
Document360 stores images and files on its own CDN. When you export a project as a ZIP, media files are included in a Media folder. When you use the API, article content contains CDN URLs pointing to Document360's domain.
Getting images into Confluence requires a specific sequence:
- Create the Confluence page (to generate a Page ID).
- Download the image binary from Document360 (from the ZIP or by fetching the CDN URL).
- Upload the file as a Confluence attachment using
POST /wiki/rest/api/content/{pageId}/child/attachmentwith theX-Atlassian-Token: nocheckheader. (developer.atlassian.com) - Update the page body to reference the new attachment using Confluence's macro syntax.
<!-- Before (Document360 HTML) -->
<img src="https://cdn.document360.io/project/images/screenshot.png" />
<!-- After (Confluence Storage Format) -->
<ac:image>
<ri:attachment ri:filename="screenshot.png" />
</ac:image>If you skip step 4, the image file will be attached to the page but will not render inline. The attachment must be uploaded to the same page that references it (or you must use cross-page attachment references, which add complexity). Confluence's default attachment size limit is 100 MB per file.
For HTML-import flows, supplemental media must live in a folder that matches the page name, and unsupported media types will be ignored. Either way, imported page content does not mean resolved media references.
Internal link rewriting
Document360 articles link to each other using URL slugs (e.g., /docs/getting-started). After migration, those slugs no longer exist. Every internal link must be rewritten to point to the correct Confluence page.
The process:
- Build a mapping table of Document360 article IDs/slugs → Confluence page IDs.
- Create all pages in Confluence (with placeholder or final content).
- Second pass: Update each page's body, replacing Document360 link references with Confluence
<ac:link>elements that reference the correct page title and space key.
This two-pass approach is necessary because you cannot reference a Confluence page that does not exist yet. The second pass consumes additional API calls (one PUT per page), so budget for this when planning around rate limits.
Common failure mode: DIY scripts that attempt a single-pass migration end up with broken internal links on every page that references another page created later in the sequence. Always plan for a two-pass approach or use a migration engine that handles deferred link resolution.
Step-by-Step: The API Migration Workflow
If you are building this yourself, here is the execution plan.
Step 1: Audit the source
Count workspaces, languages, category depth, editor types, reused-content features (snippets, variables), duplicate article titles, custom fields, and public/private docs. This determines whether the HTML importer is viable or whether you need a full API pipeline.
Check your Document360 plan: Standard has no API access, and the project ZIP export API endpoint is Enterprise-only. That combination can force a UI-export or service-led extraction plan. (apidocs.document360.com)
Step 2: Extract data from Document360
Use the Document360 API to pull the full category tree and all articles:
GET /v2/ProjectVersions→ list all project versionsGET /v2/ProjectVersions/{versionId}/categories/{langCode}→ full category tree with nested articlesGET /v2/Articles/{articleId}/{langCode}→ individual article content
For published knowledge bases, isForDisplay=true (expand snippets and variables), isPublished=true (latest published version), and appendSASToken=false (prevent SAS-signed URLs) is the cleanest starting point. (apidocs.document360.com)
Alternatively, use the ZIP export for bulk extraction. The ZIP contains a JSON manifest file that maps the category-article hierarchy, plus a Media folder with all referenced files. The UI export lets you scope by modified date, workspace, language, and category, and optionally include media. (docs.document360.com)
Step 3: Create the Confluence Space and page hierarchy
- Create one Confluence Space per Document360 Workspace.
- Create parent pages for each top-level category (include any category description text — do not leave them blank).
- Create child pages for subcategories and articles, preserving the nesting depth.
- Store a mapping table keyed by Document360 article ID, category ID, and URL as you create each page.
Step 4: Convert content to Confluence Storage Format
For each article:
- If Markdown: Parse with a Markdown-to-HTML library, then transform the HTML to valid Confluence Storage Format XML.
- If HTML: Sanitize the HTML, then transform to Storage Format — replace standard HTML tags with Confluence-specific elements where needed.
Detect the editor type from the API response. The editor_type field returns 0 for Markdown, 1 for WYSIWYG, and 2 for Advanced WYSIWYG. Branch your converter accordingly. Mixed editors in a single project mean mixed pipelines.
This step must handle: headings, paragraphs, lists (including nested lists), tables, code blocks with language hints, images, links, and callout boxes.
Step 5: Upload attachments
For each page, download all referenced media files and upload them as Confluence attachments via the attachment API. Track the filename-to-page-ID mapping for the next step.
Step 6: Rewrite links and image references
Second pass through all pages: replace Document360 CDN image URLs with <ac:image> attachment references, and replace internal article links with <ac:link> elements pointing to the correct Confluence page title and space key.
Step 7: Validate
Run a validation pass comparing:
- Total article count in Document360 vs. total page count in Confluence
- Attachment count per page
- Spot-check formatting on a representative sample — prioritize pages with code blocks, tables, nested lists, inline images, and repeated titles
- Test all internal links for 404s
Pilot with the hard cases first, not the easy ones. Pick pages with code blocks, tables, nested lists, inline images, multilingual content, and repeated titles. If you imported into a staging space, Confluence says moved items carry their attachments, comments, child items, and incoming links, and get automatic redirects. That makes staging-space validation much safer than importing directly into production. (support.atlassian.com)
Store the original Document360 article ID, category ID, URL, and last-modified date as Confluence page properties. This gives you idempotent reruns, easier QA, and a clean redirect map. (apidocs.document360.com)
Edge Cases That Cause Silent Data Loss
-
Mixed editor types in a single project. Single-language exports can contain both
.mdand.htmlfiles. Multilingual exports become.json. One parser will not cover every scenario — your converter must detect the editor type and branch accordingly. (docs.document360.com) -
Variables and snippets rendered inline. With
isForDisplay=true, variables and snippets resolve into the content body. You get the rendered version, but you lose the reuse structure. WithisForDisplay=false, you get raw tokens that will not render in Confluence. There is no option that preserves both fidelity and reuse. -
Confluence page title uniqueness. Within a single Space, all page titles must be unique. Duplicate "Overview" or "Introduction" pages from different Document360 categories will collide.
-
Right-to-left (RTL) content. Document360 supports per-article RTL settings. Confluence handles RTL via language settings, not per-page toggles. RTL articles may need manual styling adjustments post-migration.
-
64 KB page body limit. Very long Document360 articles with base64-encoded inline images can exceed this limit on the Confluence API. Convert base64 images to attachments before pushing the page body.
-
Category pages with content. Document360 categories can have their own body content. In Confluence, the equivalent parent pages must also have body content set — do not skip category descriptions or you will have blank container pages.
-
HTML import degradation. If using Confluence's native HTML importer, code blocks become plain text,
iframeelements are unsupported, equations downgrade to plain text, and embedded videos become hyperlinks. (support.atlassian.com) -
Metadata gaps. Document360 exposes workflow status, custom fields, translation status, and public/private visibility via API. Confluence can store some of these as page properties, labels, or restrictions, but it requires an explicit mapping plan. Nothing carries over automatically. (apidocs.document360.com)
DIY Scripts vs. Marketplace Apps vs. ClonePartner
DIY API scripts
Best for: Engineering teams with Python/Node experience, fewer than 200 articles, and minimal inline media.
Hidden cost: The Markdown/HTML-to-Storage-Format converter is the real time sink. Expect 2–4 weeks of engineering time for a reliable parser that handles tables, code blocks, images, and nested lists. Budget additional time for the link-rewriting second pass and the attachment pipeline. In practice, you are building a parser, an uploader, a retry engine, a mapper, and a QA harness.
Risk: Formatting bugs that are invisible until readers report them. A paragraph that renders fine in Document360 might silently drop a nested list in Confluence because the Storage Format XML was not properly closed.
Marketplace apps
Best for: Small knowledge bases (under 50 articles) with simple formatting and no inline images.
Atlassian's own import documentation points to Marketplace apps for Markdown, HTML, and related formats. These tools are generic import helpers, not Document360-aware migration engines. (support.atlassian.com) They expect clean Markdown files, but Document360's Markdown often includes HTML fragments — especially for tables, callouts, and embedded content — which marketplace importers typically pass through as raw HTML rather than converting to Storage Format. They also do not handle the category hierarchy, cross-article link rewriting, or the attachment upload workflow.
ClonePartner managed migration
Best for: Knowledge bases with 100+ articles, mixed editor types (Markdown and WYSIWYG), heavy inline media, cross-article linking, or strict cutover deadlines.
Our migration engine handles the full pipeline: Document360 API extraction → format conversion (both Markdown and HTML to Storage Format) → attachment upload → internal link rewriting → validation. We manage both Document360's token-based rate limits and Confluence's API constraints to ensure zero dropped records. For a look at how we structure migration work, see How We Run Migrations at ClonePartner.
Making the Right Call
Migrating from Document360 to Confluence is a format-conversion problem wrapped in a rate-limit-management problem. Document360's content is usually clean and well-structured. The challenge is getting that content into Confluence's proprietary format without losing formatting, breaking links, or orphaning images.
For small, text-heavy knowledge bases with under 50 articles and no inline media, a manual or semi-automated approach works. The Confluence HTML importer is a reasonable starting point if your content is already HTML.
For anything larger — mixed Markdown and WYSIWYG articles, heavy cross-linking, embedded images, multilingual content, or a strict cutover window — the engineering cost of building a reliable DIY converter usually exceeds the cost of hiring a team that has already solved this problem.
Whatever path you choose: prove title mapping, hierarchy, attachment fidelity, link rewriting, and metadata carryover in a pilot before you move the full tree. A migration that technically completes but leaves weeks of cleanup is not a successful migration.
Frequently Asked Questions
- Can I import Document360 articles directly into Confluence?
- Not directly. Confluence Cloud has an HTML importer that accepts a ZIP of .html files and creates a new space, but it cannot process Markdown or multilingual JSON exports, and code blocks degrade to plain text. For full-fidelity migration you must convert content to Confluence Storage Format (XHTML) or ADF using API scripts or a migration service.
- What data is lost when migrating from Document360 to Confluence?
- Templates, variables, snippets, glossary terms, workflow statuses, article analytics, reader accounts, interactive API documentation, and custom domain settings are not included in Document360's export and have no direct Confluence equivalent. These must be recreated manually.
- How do Document360 categories map to Confluence?
- Document360 Workspaces map to Confluence Spaces. Top-level Categories become parent pages under the Space homepage. Subcategories become nested child pages. Articles become the leaf-level pages under their category page. All pages in a Space must have unique titles.
- What are the API rate limits for a Document360 to Confluence migration?
- Document360 enforces per-token limits: 60 requests/minute on Professional and Business plans, 100 on Enterprise. Confluence Cloud's new points-based quotas (March 2026) apply to Forge, Connect, and OAuth 2.0 apps — API token traffic remains under existing burst limits. Both return HTTP 429 when exhausted.
- How long does a Document360 to Confluence migration take?
- A small knowledge base (under 50 articles, minimal media) can be migrated manually in 1–2 days. For 200+ articles with inline images and cross-linking, a DIY API approach takes 2–4 weeks of engineering. A managed migration service like ClonePartner can typically complete the same scope in days.