Launched:self-serve migrations intoSuperhuman Docs (Coda)
Try it now
01Agent-first
Runs where you already work
Plug it into Claude, ChatGPT or Cursor. Describe the move in plain English; the agent runs it.
02Engineer-led
Our production engine, unlocked
The pipeline our engineers use on managed enterprise migrations — the same code, now something you can drive yourself.
03Pricing
Try 10 pages free, then $1 a page
Credit-based, pay-as-you-go. No scoping call, no quote — sample it on your own docs before you spend anything.
04Sources
NotionSlabConfluenceSoonGoogle DocsSoon
Skip to content

Archbee to Slack Canvas Migration: The Technical Guide

No import path exists from Archbee to Slack Canvas. Technical guide covering extraction, block-by-block mapping, structure redesign, and API rate limits.

Abdul Aleem Abdul Aleem · · 19 min read
Archbee to Slack Canvas Migration: The Technical Guide
TALK TO AN ENGINEER

Planning a migration?

Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.

Schedule a free call
  • 1,500+ migrations completed
  • Zero downtime guaranteed
  • Transparent, fixed pricing
  • Project success responsibility
  • Post-migration support included

Archbee to Slack Canvas Migration: The Technical Guide

There is no import button, no plugin, and no middleware that moves Archbee content into Slack Canvas. Archbee stores document bodies in a proprietary JSON block format — not Markdown, not HTML — so every migration requires a custom converter that extracts Archbee's block structure and emits the specific subset of Markdown that Canvas accepts. This is converter engineering, and the sooner a team acknowledges that, the fewer surprises they'll face mid-project. (archbee.com)

Archbee is hierarchical by design: workspaces contain spaces, spaces contain document trees with nested categories acting as folders. Archbee's editor exposes 30+ custom block types — vertical splits, OpenAPI/Swagger, Mermaid diagrams, tabs, changelogs, and more. Slack Canvas is much narrower. The API accepts a document_content object whose only supported type is markdown, and Slack explicitly states that Block Kit is not supported in canvases. That structural and format mismatch is why a straight export-and-paste results in broken formatting, lost metadata, and missing content. (docs.slack.dev)

This guide covers the extraction path from Archbee, the block-by-block mapping decisions your converter must make, the Archbee features that have no Canvas equivalent, the structural redesign from spaces to channels, the rate limits (documented and otherwise) you'll hit on the Slack side, and an end-to-end migration runbook.

How to get content out of Archbee

Archbee's bulk Markdown export is the pragmatic starting point. You can export an entire space as a Markdown ZIP archive — categories become folders, each page becomes a separate .md file — or export the full organization from Organization Settings, which emails admins a ZIP of everything. Individual documents can also be exported to Markdown or PDF from the document options menu. (archbee.com)

The alternative is reading documents through the Archbee API. Authentication uses a bearer token obtained from your Archbee organization settings (Settings → API Keys). The required scope is docs:read for document retrieval. GET https://api.archbee.com/api/public-api/doc with a docId parameter returns the document's content in Archbee's proprietary block format. The API supports a format query parameter accepting markdown, html, json, or source, giving you more control but requiring you to parse every block type yourself, one document at a time. Space listing is available via GET /api/public-api/spaces, and document enumeration within a space via GET /api/public-api/docs?spaceId={id}. Pagination uses cursor-based navigation: each response includes a nextCursor field; pass it as cursor in subsequent requests until no cursor is returned.

For most migrations, the bulk Markdown export is the better entry point because Archbee's own renderer has already handled the block-to-text conversion. Your converter script can download the resulting .zip, read the Markdown files, and focus entirely on translating that Markdown into Slack Canvas's specific flavor.

Warning

The Markdown export is lossy. Archbee's export strips or flattens features that don't have a Markdown representation: variables render as their current value (or as raw {{tokens}} if unresolved), OpenAPI reference pages export as a basic description rather than the interactive Swagger UI, expandable headings lose their collapse behavior, and custom widgets like the documentation widget and changelog are omitted entirely. You'll need a separate pass to decide what to do with those.

Block types by export quality

The following table specifies which Archbee block types export cleanly via Markdown, which export degraded, and which require API access or alternative handling:

Archbee block type Markdown export quality Recommended extraction method
Paragraph, bold, italic, strikethrough Clean Markdown export
Headings h1–h3 Clean Markdown export
Headings h4–h6 Degraded (clamped to h3 or body text) Markdown export + converter post-processing
Bulleted/ordered lists Clean Markdown export
Checklists Clean Markdown export
Expandable headings Degraded (flattened, behavior lost) Markdown export; document loss
Code blocks (single language) Clean Markdown export
Code blocks (multi-tab) Degraded (tabs collapsed to first or all) API (json format) for tab structure
Code drawer (two-column) Degraded (layout lost, code preserved) Markdown export; note layout loss
Inline code Clean Markdown export
Tables (≤300 cells) Clean Markdown export
Tables (>300 cells) Truncated or rejected API + manual split logic
Blockquotes Clean (flat only) Markdown export
Callouts (info/warning/tip) Partially clean Markdown export + syntax rewrite
Vertical split / column layout Degraded (linearized) Markdown export; note layout loss
Internal document links Broken (Archbee docId paths) API; requires link rewriting map
User mentions Degraded (username text, not Slack ID) API; requires user-mapping table
Mermaid diagrams Omitted Export as image via API or screenshot
draw.io embeds Omitted Export as image
iframe embeds (YouTube, etc.) Omitted Replace with hyperlink
OpenAPI/Swagger pages Omitted (description stub only) Link to external spec host
Variables ({{token}}) Partially resolved or raw token Pre-resolve before conversion
Documentation widget Omitted Keep in-product; out of scope for Canvas
Changelog block Omitted Convert to reverse-chronological h2 list

When to use the API instead

For small workspaces or when you need block-level metadata, reading the API directly is viable. Archbee returns the document body as a JSON array of typed blocks — headings, paragraphs, code editors, API endpoints, callouts, vertical splits, and more. Each block has a type field and type-specific properties. The problem is scale: you need to enumerate every document in every space, handle cursor pagination, respect Archbee's rate constraints, and write a parser for every block type you encounter.

The pragmatic approach: use the bulk export as your primary source and fall back to the API only for block types the export doesn't handle well (multi-tab code blocks, large tables, internal link resolution). Keep PDF export as an archive lane for pages that are mostly generated content (OpenAPI pages, widget-driven pages) where Markdown conversion would produce unreadable output. (archbee.com)

What Slack Canvas actually accepts

Slack Canvas content is Markdown — not mrkdwn, not Block Kit, not HTML. When you create or edit a canvas via the API, you provide a document_content object with "type": "markdown" and a markdown string. That string is limited to 1 MiB (1,048,576 characters) per call. Any payload containing unsupported syntax is rejected with an HTTP 400 error. (docs.slack.dev)

The supported Markdown elements, per the official Slack developer docs:

Element Canvas support Syntax
Headings h1, h2, h3 only #, ##, ###
Bold Yes **text**
Italic Yes _text_
Strikethrough Yes ~text~
Bulleted lists Yes - item
Ordered lists Yes 1. item
Checklists Yes - [ ] task / - [x] done
Code blocks Yes (fenced) ``` code ```
Code spans Yes `inline`
Blockquotes Yes > text
Callouts Yes Slack-specific syntax
Dividers Yes ---
Column layout Yes (flexbox) Slack-specific
Links Yes [text](url)
Tables Yes, 300-cell max Pipe syntax
Mentions Users and channels ! [](@U123) / ! [](#C123)
Unfurls Canvas, message, website, file Auto from URLs

Nothing else is supported. If your script attempts to push a Block Kit JSON payload into a Canvas update, the API will fail.

On the 300-cell table limit: Slack counts all body cells plus header cells. A table with 1 header row and 29 data rows across 10 columns = 300 cells total — at the exact limit. A 31-row × 10-column table (310 cells) will be rejected. Enforcement is per-table, not per-canvas, so splitting one large table into two smaller ones resolves the issue without affecting other tables in the same canvas.

Info

Undocumented constraint: the canvases.edit method's changes array accepts only one change per request. Passing multiple operations in a single call fails with "no more than 1 items allowed". This is not mentioned in Slack's official documentation but is consistently enforced. Plan your pipeline to make one edit call per section. (docs.slack.dev)

Block-by-block mapping: what the converter decides

Whether you're processing the Markdown export or parsing the raw JSON blocks, your converter must make a mapping decision for every content type. Here's what each Archbee block type maps to — and where the mapping breaks.

Text and headings

Archbee supports heading levels 1 through 6. Canvas supports h1 through h3 only. Your converter needs to clamp h4–h6 down to h3, or demote them to bold text. Paragraphs and inline formatting (bold, italic, strikethrough) map directly.

Archbee's expandable headings — collapsible sections triggered by a >> prefix — have no Canvas equivalent. Flatten them into standard headings; the expand/collapse behavior is lost.

Lists and checklists

Bulleted and ordered lists map directly. Archbee checklists (- [ ] syntax) map to Canvas checklists. Nested lists work in most cases, but deeply nested structures (more than two or three levels) can cause Canvas to silently flatten or reject content.

Danger

Nested structures inside blockquotes are rejected. A bulleted list inside a blockquote — valid in standard Markdown and in Archbee — will trigger a canvas_editing_failed error in Slack. Your converter must detect this pattern and either extract the list outside the quote or flatten it to plain text within the quote. (docs.slack.dev)

For example, if Archbee exports this:

> Here is a list:
> * Item 1
> * Item 2

Your converter must flatten it into sequential blocks:

> Here is a list:
 
* Item 1
* Item 2

Every regex or syntax tree parser in your converter must actively detect and flatten unsupported nesting — not as an afterthought but as a core step.

Code blocks and code spans

Archbee's multi-language code editor block (supporting 30–40 languages with syntax highlighting) maps to a fenced code block in Canvas. Canvas supports language tags on fenced blocks, but syntax highlighting is minimal. If an Archbee code editor contains multiple tabbed language examples, you must decide: emit them all sequentially, pick one, or use a heading to label each variant. When you need to preserve tab structure, fetch the block via the API using format=json — the tabbed structure is explicit in the block's JSON, whereas the Markdown export collapses or discards it.

Archbee's code drawer — the two-column layout with scrolling code on the right — has no direct equivalent. The code itself converts fine; the side-by-side presentation is lost.

Inline code spans map directly with backtick syntax.

Blockquotes and callouts

Archbee callout blocks (info, warning, tip variants) map to Canvas callouts, which use their own Slack-specific syntax. Standard blockquotes (>) map directly. The key constraint: keep callout and quote content flat. No nested lists, no nested blockquotes.

Tables

Archbee tables map to Canvas Markdown tables using pipe-and-dash syntax. Canvas enforces a 300-cell ceiling per table — rows × columns including the header row must not exceed 300. A 10-column table can have at most 29 data rows (30 rows × 10 columns = 300). If your Archbee docs contain large data tables, your converter must either truncate them or split them into multiple tables, each under the 300-cell limit.

Cells support bold, italic, strikethrough, code, links, lists, checkboxes, and mentions. Merged cells (column or row spans) are not supported — flatten them to the most specific value. (docs.slack.dev)

Column layouts

Archbee's vertical split block creates multi-column layouts. Canvas supports column layout via flexbox, but the Markdown syntax for it is Slack-specific and not well-documented. Simple two-column layouts can work if you follow the internal syntax; anything more complex (three columns, nested splits) may not render correctly. The safe fallback is to linearize the content: left column first, then right column, separated by a divider.

Inline links ([text](url)) map directly. Archbee's internal document links — using dynamic linking that resolves to a docId — will break. Canvas has no concept of Archbee document IDs.

When you create a canvas via canvases.create, the API response includes both a canvas_id (format: F followed by an alphanumeric string, e.g., F012AB3CDEF) and a url field containing the fully-qualified Slack URL for that canvas (e.g., https://yourworkspace.slack.com/canvas/F012AB3CDEF). Store both in your local mapping table keyed by original Archbee docId. During the link-rewriting pass, replace every archbee.com/... internal path with the corresponding Canvas URL from your map. Links to Archbee documents not included in the migration scope should be replaced with plain text or a note indicating the content was not migrated.

Do not leave dead Archbee paths in migrated docs.

Mentions

Archbee user mentions won't carry over directly. Canvas expects Slack user IDs in the format ! [](@U123ABCDEFG). To preserve mentions, build a user-mapping table using the following approach:

  1. Extract all Archbee mention references from the export (username or email format, depending on your Archbee configuration).
  2. Call users.list against the Slack API (Tier 2, paginated via cursor). Each user object includes id (the UXXXXXXXX Slack member ID), name, real_name, and profile.email.
  3. Match Archbee mentions to Slack users by email where available, falling back to display name. Store the resulting archbee_username → slack_member_id map.
  4. In the converter, replace each Archbee mention with ! [](@{slack_member_id}).

Without this mapping, convert mentions to plain text with the person's name. Do not leave unresolved Archbee mention syntax in Canvas content.

Diagrams and embeds

Archbee's Mermaid diagram block, draw.io embeds, and iframe embeds (YouTube, Vimeo, maps) have no Canvas equivalent. Canvas does not render diagrams or embedded media. Your options for diagrams:

  1. Export as image and upload via files.upload (or the newer files.getUploadURLExternal + files.completeUploadExternal flow for files >1 MB). The returned file.permalink or file.permalink_public URL can be embedded in Canvas Markdown as a standard link — Canvas will unfurl image files shared within the workspace. Use - ! [Alt text](file_url) syntax if the file is in the same workspace.
  2. Replace with a hyperlink to the original Mermaid source, draw.io file, or video URL. Canvas will auto-unfurl recognized domains.

For iframes (YouTube, Vimeo), replace with a plain URL on its own line — Slack Canvas auto-unfurls YouTube and Vimeo URLs into preview cards without requiring any special syntax.

Dividers

Horizontal rules (---) map directly. No conversion needed.

Which Archbee features have no Canvas equivalent?

Several Archbee features don't just lose formatting in Canvas — they have no target at all. Your migration plan needs an explicit decision for each.

Variables

Archbee variables are reusable tokens (defined with {{variable_name}} syntax) that substitute their stored value at render time. Variable names can be up to 40 characters and values up to 100 characters. Variables can be global or space-specific. The placeholder semantics matter, not just the rendered text — if a variable name changes in Archbee, old references stop rendering. (archbee.com)

In the Markdown export, variables either render as their current resolved value or appear as raw {{token_name}} strings. Canvas has no variable substitution system. If your docs rely on variables to keep product names, URLs, or version numbers consistent:

  1. Pre-resolve all variables before conversion, locking in current values.
  2. Document which strings were formerly variables, so future updates require manual find-and-replace across canvases.
  3. Audit for raw tokens. After conversion, grep the output for {{ and }} — any remaining unresolved tokens will appear as literal text in Canvas and should be resolved or removed before publishing.

OpenAPI reference pages

Archbee auto-generates interactive API reference pages from OpenAPI/Swagger spec files, complete with a Swagger UI component that lets users make live requests. Imported OpenAPI files cannot be manually modified in Archbee — this is an entire rendered application, not static content. (archbee.com)

Canvas cannot host interactive API references. Your choices:

  • Link out to the spec hosted elsewhere (Swagger UI, Redocly, Stoplight).
  • Extract endpoint summaries and render them as static Markdown tables (method, path, description, parameters) — useful for quick reference but a significant downgrade.
  • Skip these pages entirely if the team maintains a separate API docs portal.

Documentation widget

Archbee's contextual documentation widget embeds docs directly inside a product's UI via JavaScript. This is a feature of Archbee's hosting platform, not a document type. Canvas is a Slack document surface — it does not replace in-app contextual help. Keep the widget where it lives, or replace it with another in-product docs mechanism. (archbee.com)

Changelog entries

Archbee includes a changelog block for logging version updates (added, fixed, improved, broken). These are structured entries, not free-form text. Canvas has no changelog concept. Convert them to a reverse-chronological Markdown document with h2 headers for each date, or maintain changelogs in a separate tool. (archbee.com)

If a page is mostly one of these non-equivalent features, exporting it as PDF and linking the archive from an index canvas is often cheaper than forcing a fake one-to-one conversion.

How does Archbee's structure map to Slack channels?

Canvas has no folder hierarchy. This is the biggest structural mismatch. Archbee organizes content into workspaces → spaces → document trees (with categories acting as folders and documents nested in a tree). Slack Canvas has two types: standalone canvases (owned by a user, shareable) and channel canvases (one per channel, pinned to the channel). A channel holds exactly one channel canvas. There is no nesting, no sub-pages, no table-of-contents tree.

Archbee concept Slack target Notes
Workspace Slack workspace 1:1 if single workspace
Space Slack channel One channel per Archbee space
Top-level category Channel canvas (index) Becomes the navigation layer
Document (leaf page) Standalone canvas Linked from the index
Nested sub-documents Standalone canvases Flat — no hierarchy
Folder-only category H2 section in index Do not create empty canvases
OpenAPI or widget page Link, unfurl, or PDF archive No true Canvas equivalent

The practical pattern:

  1. Create one Slack channel per Archbee space. Name it to match the space.
  2. Create a channel canvas as the index page. This canvas lists and links to all standalone canvases representing the space's documents.
  3. Create a standalone canvas for each document. Use canvases.create with the converted Markdown content. The API response includes both canvas_id and url — store both in a local mapping table keyed by Archbee docId.
  4. Build the index. Once all standalone canvases exist and you have their URLs, generate index Markdown with [Document Title](canvas_url) links and push it to each channel canvas via canvases.edit.

This is imperfect. Users lose the sidebar navigation tree they had in Archbee. The index canvas is a flat list of links. For large spaces with 50+ documents, this gets unwieldy — consider splitting into multiple channels or using Slack's bookmark bar for high-traffic pages.

Warning

Standalone canvases require a paid Slack plan. Channel and DM canvases are available on all plans, but standalone canvases are only available on paid plans. If you're on a free workspace, design around a channel canvas and linked resources instead of a many-canvas knowledge base. (slack.com)

What rate limits will you hit?

Slack's rate limits are tiered per method, per workspace, per app. The documentation publishes minimum rates but not exact burst ceilings.

Method Tier Published minimum
canvases.create Tier 2 20+ requests/min
conversations.canvases.create Tier 2 20+ requests/min
canvases.edit Tier 3 50+ requests/min
canvases.sections.lookup Tier 3 50+ requests/min
canvases.getContent Tier 3 50+ requests/min
canvases.access.set Tier 3 50+ requests/min
users.list Tier 2 20+ requests/min
conversations.create Tier 2 20+ requests/min

At Tier 2 rates, creating 200 standalone canvases takes a minimum of 10 minutes — assuming no bursts and no retries. Slack tolerates short bursts above the published minimum before returning HTTP 429 with a Retry-After header; the actual ceiling is not published. Staying at approximately 1 request per 3 seconds (20/min) keeps migrations below the threshold that triggers sustained 429 responses. The Retry-After value is in seconds and should be used directly — do not use a fixed sleep when Slack has told you the exact wait. (docs.slack.dev)

The one-change-per-request problem

The canvases.edit changes array is documented as accepting a list of operations, but Slack enforces a limit of exactly one change per request. If a canvas needs three edits (insert content, add a section, rename), that's three API calls, each rate-limited independently. For a migration touching hundreds of canvases, this multiplies your API call count significantly. Budget 3–5 canvases.edit calls per canvas for a typical document (initial content insert, access permissions, plus any section operations).

Concurrent edit locks

Sending concurrent canvases.edit requests to the same Canvas ID can trigger a canvas_editing_locked error. Use a single-writer queue per canvas — never send parallel writes to the same canvas. Parallel writes to different canvases are safe within rate limits. (docs.slack.dev)

Throttling strategy

import time
import requests
 
def create_canvas_with_backoff(token, title, markdown, max_retries=5):
    url = "https://slack.com/api/canvases.create"
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    payload = {
        "title": title,
        "document_content": {"type": "markdown", "markdown": markdown}
    }
    for attempt in range(max_retries):
        resp = requests.post(url, json=payload, headers=headers)
        data = resp.json()
        if resp.status_code == 429:
            wait = int(resp.headers.get("Retry-After", 10))
            time.sleep(wait)
            continue
        if data.get("ok"):
            return data["canvas_id"], data["url"]  # store both for link rewriting
        raise Exception(f"Canvas create failed: {data.get('error')}")
    raise Exception("Max retries exceeded")
 
 
def build_user_mapping(token):
    """Returns dict of {archbee_email: slack_member_id}"""
    users = {}
    cursor = None
    while True:
        params = {"limit": 200}
        if cursor:
            params["cursor"] = cursor
        resp = requests.get(
            "https://slack.com/api/users.list",
            headers={"Authorization": f"Bearer {token}"},
            params=params
        )
        data = resp.json()
        if not data.get("ok"):
            raise Exception(f"users.list failed: {data.get('error')}")
        for member in data.get("members", []):
            email = member.get("profile", {}).get("email")
            if email:
                users[email] = member["id"]
        cursor = data.get("response_metadata", {}).get("next_cursor")
        if not cursor:
            break
        time.sleep(1)  # users.list is Tier 2; stay under limit
    return users

Key principles:

  • Respect Retry-After headers. Don't guess — the header tells you exactly how long to wait.
  • Use exponential backoff for non-429 transient errors.
  • Separate create and edit queues. They're on different tiers with different rate budgets.
  • Run the migration during off-peak hours for the target Slack workspace to maximize available burst capacity.
  • Log every failure with the full response body. A canvas_editing_failed error on one document almost always means the Markdown in that specific document has an unsupported nesting pattern. The error message will not identify which element failed — you must bisect the Markdown to find it.
  • Read back what you wrote. canvases.getContent returns the full canvas as Markdown or HTML — use it for automated diffing and spot QA. (docs.slack.dev)

The migration pipeline

  1. Inventory the corpus. Use Archbee's space listing (GET /api/public-api/spaces) and document enumeration (GET /api/public-api/docs?spaceId={id}, cursor-paginated) to enumerate all documents. Tag each page as direct-convert, flatten, or archive. Flag variables, OpenAPI pages, widget-driven docs, changelogs, large tables (>300 cells), multi-tab code blocks, and folder-only categories before writing conversion code.
  2. Export in lanes. Pull Markdown for standard docs and PDF for generated or non-equivalent pages. Retain source docId values and original paths so link rewriting stays deterministic. (archbee.com)
  3. Pre-process variables and mentions. Resolve all {{variable}} tokens to their current values; grep for remaining {{ patterns after substitution. Build a user-mapping table from Archbee emails to Slack member IDs using the users.list API (see code sample above).
  4. Build and run the converter. Walk each Markdown file, validate against Canvas's supported element list, and transform incompatible patterns: clamp h4–h6 headings, extract lists from blockquotes, truncate or split tables over 300 cells, linearize column layouts, and resolve internal links.
  5. Map structure. Define which Archbee spaces become which Slack channels. Create channels via conversations.create if they don't exist.
  6. Create leaf canvases first. Use canvases.create for standalone canvases, throttled at approximately 1 request per 3 seconds. Store both canvas_id and url from each response in a mapping table keyed by Archbee docId. Handle HTTP 429 responses using the Retry-After header directly.
  7. Build index canvases. Once all standalone canvases exist and you have their URLs, generate index Markdown with [Document Title](canvas_url) links and push it to each channel canvas via conversations.canvases.create and canvases.edit. Remember: one change per canvases.edit call.
  8. Validate. Read back canvases with canvases.getContent and spot-check at least 10% of converted content. Look for: flattened headings (h4+ that should have been demoted intentionally), broken links (remaining archbee.com paths), raw variable tokens ({{), empty sections where widgets were stripped, and tables that were silently truncated at 300 cells.
  9. Archive exceptions. Link out to PDFs or external API references where Canvas has no true equivalent. Don't leave dead ends.
  10. Communicate the cutover. Canvases don't support redirects. Old Archbee URLs will 404 after decommissioning. Share the new index channels with every team and update any external links pointing to Archbee docs.

What this migration does not give you

Be honest with stakeholders about what's lost:

  • No search parity. Archbee's full-text search across spaces is replaced by Slack's general search, which indexes canvas content but doesn't offer the same faceted, documentation-specific experience.
  • No versioning. Archbee tracks document revision history. Canvas has no version history API — once content is overwritten, the previous version is gone. There is no way to recover a previous canvas state through the API.
  • No publishing workflow. Archbee's preview → production publish cycle, with staging environments, doesn't exist in Canvas. A canvas is live the moment it's created.
  • No analytics. Archbee provides page view and search analytics. Canvas provides none.
  • No SEO. Archbee spaces can be published to custom domains with SEO controls. Canvases are internal to Slack and not indexable by search engines.
  • No granular permissions per document. Archbee supports per-document access controls. Canvas permissions are set at the canvas level via canvases.access.set, but the permission model is simpler: workspace members, specific users, or everyone with the link.

If the team needs any of these capabilities, Canvas may not be the right migration target — or it may be the right target for internal docs only, with a separate tool handling customer-facing content.

Effort estimation

A team with strong scripting skills can expect the following rough timelines:

Corpus size Variable/widget usage Estimated converter build + migration time
<50 documents, simple content Low 1–3 days
50–200 documents, moderate complexity Medium 1–2 weeks
200–500 documents, heavy variables/OpenAPI High 3–6 weeks
500+ documents, complex layouts High 6–12 weeks

The largest time sinks are: (1) building and testing the block-type mapping table for edge cases, (2) manual triage of non-equivalent content (OpenAPI pages, changelogs, widget-driven pages), and (3) link rewriting when Archbee documents cross-reference each other heavily. API rate-limit throttling adds calendar time but not engineering time if the backoff logic is built correctly from the start.

If you'd rather spend that time on product work, ClonePartner has built converters for block-based editors, proprietary JSON formats, and constrained target APIs. We handle the block-by-block mapping, the rate-limit throttling, the structure flattening, and the edge-case triage.

Frequently Asked Questions

Can I import Archbee content directly into Slack Canvas?
No. There is no native import, plugin, or middleware. Archbee stores content in a proprietary JSON block format, and Slack Canvas accepts only Markdown via its API. You must build a custom converter or use a bulk Markdown export as an intermediate step.
What Archbee features are lost when migrating to Slack Canvas?
Variables (render-time substitution), OpenAPI/Swagger interactive reference pages, the documentation widget, changelog entries, expandable headings, Mermaid diagrams, iframe embeds, version history, the publish workflow, page analytics, and SEO controls are all lost. Canvas has no equivalent for any of these.
What are the Slack Canvas API rate limits for bulk migration?
canvases.create is Tier 2 (20+ requests per minute) and canvases.edit is Tier 3 (50+ per minute). An undocumented constraint limits canvases.edit to one change per request. At Tier 2 rates, creating 200 canvases takes at least 10 minutes. Always respect the Retry-After header on 429 responses.
How do Archbee spaces map to Slack channels and canvases?
Slack Canvas has no folder hierarchy. The practical approach is one Slack channel per Archbee space, a channel canvas as the index/table of contents, and one standalone canvas per document. Nested document trees are flattened. Standalone canvases require a paid Slack plan.
Does Slack Canvas support Block Kit or HTML content?
No. Canvas content must be standard Markdown — not mrkdwn, not Block Kit, not HTML. Supported elements include h1–h3 headings, bold, italic, strikethrough, lists, checklists, code blocks, quotes, callouts, dividers, links, tables (300-cell max), and mentions. Content is capped at 1 MiB per API call.

More from our Blog