---
title: "Slack Canvas to Guru Migration: A Verification-First Guide"
slug: slack-canvas-to-guru-migration-a-verification-first-guide
date: 2026-10-01
author: Raajshekhar Rajan
categories: [Slack Canvas, Guru, Migration Guide]
excerpt: "Migrating Slack Canvas to Guru is mostly editorial: every canvas needs a verifier, interval, and trust state that don't exist in the source. Here's how."
tldr: "Slack Canvas to Guru migration is 80% editorial (triage, verifiers, intervals) and 20% scripting. Plan the verification model before you write any code."
canonical: https://clonepartner.com/blog/slack-canvas-to-guru-migration-a-verification-first-guide
---

# Slack Canvas to Guru Migration: A Verification-First Guide


# Slack Canvas to Guru Migration: A Verification-First Guide

Migrating Slack Canvas to Guru is 80% editorial work and 20% scripting. The technical conversion — markdown to HTML, channels to collections, files to cards — is straightforward engineering. The hard part is that every Guru card requires a **verifier**, a **verification interval**, and carries a visible **trust state** (either `TRUSTED` or `UNVERIFIED`), and none of that metadata exists in Slack Canvas. Somebody has to decide who owns the accuracy of each piece of content and how often it gets reviewed. That decision is human, not technical, and at volume it is the bottleneck that stalls the entire migration.

If your real goal is searchable Slack context rather than curated, durable knowledge, test Guru's Slack Source before committing to a full card migration. Guru can index selected Slack channels and keep them updated near real time, which is often a better fit for working notes than turning every canvas into a verified card. ([help.getguru.com](https://help.getguru.com/docs/setting-up-slack-as-a-source))

This guide covers how to triage which canvases deserve card status, assign verification ownership without derailing the project, extract canvases from the Slack API, convert content and handle edge cases, handle access permissions, avoid common rollback scenarios, and deal with what cannot be migrated.

> [!WARNING]
> If you skip the verifier and review-cadence decisions, you do not finish the migration faster. You just move the backlog into Guru, where it becomes a verification queue with no owner.

## Why This Migration Is an Editorial Project

**Slack Canvas** is a [lightweight document surface built into Slack](https://clonepartner.com/blog/blog/slack-canvas-alternatives-2026-features-limits-and-migration). A canvas has a title, markdown content, and an author. There is no concept of content ownership beyond "who created it," no verification workflow, and no expiration signal.

**Guru's** entire architecture revolves around trust. Every card has a verification status — `TRUSTED` or `UNVERIFIED` — plus a timestamp and an assigned verifier, so anyone reading it knows whether it has been recently reviewed. Verification is a core part of how Guru keeps knowledge trustworthy. You can reduce the frequency by setting a custom verification date up to 10 years in the future, but you cannot turn it off entirely.

This asymmetry (which also makes the [reverse migration from Guru to Slack Canvas](https://clonepartner.com/blog/blog/guru-to-slack-canvas-migration-api-limits-trust-state) a trust-model downgrade) means a script can convert markdown to HTML, but it cannot decide that the "Q3 OKRs" canvas should be verified quarterly by the VP of Product while the "Onboarding Checklist" needs monthly review by the People Ops team lead. If you bypass this by assigning every migrated canvas to a single "Migration Service Account" with a default 90-day verification interval, the migration will technically succeed. Within three months, that service account will receive thousands of verification requests, the requests will be ignored, and your Guru Trust Score will plummet — destroying user confidence in the platform.

## Which Canvases Are Worth Migrating?

Not every canvas belongs in Guru. Slack canvases fall into two categories:

- **Channel canvases** — each channel and direct message comes with its own canvas, providing a centralized hub for collecting resources. In practice, these are often meeting agendas, brainstorm dumps, and scratch-pad content. Slack began converting existing channel and DM canvases to canvases in tabs on April 9, 2025, so older inventories may mix legacy channel-canvas language with tabbed-canvas reality.
- **Standalone canvases** — created independently, often more deliberate. Product specs, runbooks, process documentation.

Guru cards are short, searchable knowledge units organized within folders and collections, with built-in collaboration and version tracking. A canvas that is meeting notes from March does not need to become a verified card that someone reviews quarterly. (If you simply need to preserve historical canvases for compliance, consider an [archival migration to Box](https://clonepartner.com/blog/blog/slack-canvas-to-box-the-complete-archival-guide) instead.)

**Good card candidates:**
- Onboarding checklists that still matter after the week they were written
- SOPs, runbooks, and escalation paths
- Policy summaries and internal FAQ material
- Channel canvases that mostly answer "how do we do X?"

**Poor card candidates:**
- Meeting notes, standup notes, retros, and brainstorms
- One-off incident scratchpads once the incident is closed
- Weekly planning docs with date-stamped content
- Canvases whose value is the conversation around them, not the document itself

**Mixed candidates** — project canvases with one durable section and five temporary sections, channel home canvases that combine evergreen links with rotating updates, or incident canvases whose postmortem section should become a card but whose live log should not. Split these during editorial review rather than migrating them wholesale.

**Triage framework:**

| Signal | Card-worthy? | Typical action |
|---|---|---|
| Referenced in multiple channels | Yes | Migrate as standalone card |
| Has >5 unique viewers | Likely | Migrate, assign topic SME as verifier |
| Last edited >6 months ago | Investigate | Archive or migrate with long interval |
| Channel canvas, <500 chars | Rarely | Skip or consolidate |
| Contains process/policy language | Yes | Migrate with short verification interval |
| Contains temporal data (dates, names) | Maybe | Migrate only if someone will maintain it |

**Triage spreadsheet schema:** Export your canvas inventory (covered below) and add these columns before writing any code:

| Column | Values |
|---|---|
| `canvas_id` | Slack file ID |
| `canvas_title` | From file object `name` field |
| `channel_id` | From `linked_channel_id` |
| `is_channel_space` | Boolean from file object |
| `last_edited` | Unix timestamp, converted to date |
| `char_count` | Approximate from content length |
| `migrate` | Y / N / Split |
| `proposed_verifier_email` | Individual or group email |
| `proposed_interval_days` | 30 / 90 / 180 / 365 |
| `proposed_collection` | Target Guru collection name |
| `proposed_folder` | Target folder within collection |
| `notes` | Free text for edge cases |

Distribute this spreadsheet to team leads for review. Give them 5 business days to complete their rows; apply defaults and move forward if they do not respond. Migrations that wait for perfect metadata never finish.

> [!TIP]
> At volume, do not ask reviewers to read every canvas from scratch. Pre-label likely keep, migrate, and archive candidates using signals like channel linkage, title patterns, last edit date, and whether the canvas reads like instructions rather than a log.

## How Do You Assign Verifiers at Volume Without Stalling?

Verification assignment is the single biggest risk of the project stalling. If you ask individual contributors to claim ownership of migrated content, you will get crickets. Nobody volunteers for ongoing review obligations.

**What works in practice:**

1. **Default to the team lead.** For each Slack channel that produced canvases, assign the channel's purpose owner (usually the team lead) as the default verifier. This is a starting point, not a final answer.

2. **Use Guru groups, not individuals.** If a group is a verifier, the verification task appears in every group member's queue until someone in the group verifies the card. This distributes load and avoids single-point-of-failure when someone leaves the company.

3. **Pick the interval in bulk by collection, not per card.** Rather than deciding card A needs 30-day review and card B needs 90, set a collection-level default: engineering runbooks get 90-day intervals, sales playbooks get 30-day, company policies get 180-day. Refine later. The `verificationInterval` field takes a number of days; passing `null` defaults to 30 days.

4. **Set verification during card creation.** The `POST /api/v1/cards/extended` endpoint accepts `verificationInterval` and `verifiers` in the same request body, so you do not need a separate call after card creation.

5. **Route only ambiguous items to humans.** If ownership is clear and the content class is obvious, do not stop the pipeline. Send only disputed or low-confidence canvases into an editorial review queue.

6. **Plan for verifier turnover before go-live.** Confirm with your Guru admin: when a verifier leaves the company, their assigned cards do not automatically reassign. Cards they owned will accumulate in the `UNVERIFIED` state until someone manually reassigns them. The mitigation is to make groups — not individuals — the primary verifier for every collection. Individual verifiers are acceptable only for cards with a single unambiguous expert owner and a long interval (180+ days).

## How to Inventory Slack Canvases via the API

There is no dedicated `canvases.list` endpoint. To programmatically list canvases, use `files.list` with the `types=canvas` filter:

```bash
curl -s "https://slack.com/api/files.list?types=canvas&count=100" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" | jq '.files[] | {id, name, created, channels, is_channel_space, linked_channel_id}'
```

Key technical details:

- **Pagination**: `files.list` defaults to 100 results per page. Use `count` and `page` parameters to iterate. For a workspace with 2,000 canvases, this is roughly 20 calls.
- **Rate tier**: `files.list` is Tier 3 (50+ requests per minute minimum), so a full inventory of 2,000 canvases is well within limits. ([api.slack.com](https://api.slack.com/methods/files.list))
- **Channel association**: Each file object includes a `channels` array showing where the canvas was shared, plus `linked_channel_id` and `is_channel_space` for channel canvases. Capture all three for triage and collection mapping.
- **Canvas content**: To retrieve the actual markdown content of a canvas, use `files.info` with the file ID. The response includes the `document_content` object with `type: "markdown"` and the full `markdown` body. ([api.slack.com](https://api.slack.com/methods/files.info))
- **Version history**: Slack stores canvas revision history internally, but the API does not expose individual historical revisions through `files.list` or `files.info`. You receive only the current version. If you need a record of prior states, export the workspace before migration — workspace exports include the current canvas version as HTML, but revision history is not included in standard exports either. Treat version history as data that will not survive this migration and communicate that to stakeholders.

> [!NOTE]
> If you have Slack export rights, workspace exports include the current version of a canvas as HTML, plus references to anchored comments and embedded files. Exports are useful for QA and historical capture even if your primary inventory path is the API. ([slack.com](https://slack.com/help/articles/15708101445011-How-data-management-features-apply-to-canvases-and-lists))

## Data Model: Slack Canvas → Guru Card

| Slack Canvas concept | Guru equivalent | Notes |
|---|---|---|
| Canvas title | Card `preferredPhrase` | 1:1 mapping |
| Canvas body (markdown) | Card `content` (HTML or Markdown) | Conversion required; see below |
| Channel | Collection or Folder | Decision: flat or nested? |
| Canvas creator | Card `owner` | Closest equivalent |
| _(does not exist)_ | `verifiers` | Must be decided by humans |
| _(does not exist)_ | `verificationInterval` | Must be decided by humans; `null` defaults to 30 days |
| _(does not exist)_ | `verificationState` | All cards start as `TRUSTED` on creation |
| Canvas comments | _(see below)_ | Cannot map to inline comments via API |
| Embedded images | Card images | Must be re-hosted; Slack URLs return 403 without auth |
| Tables (≤300 cells) | HTML tables | No formulas in source |
| Version history | _(not preserved)_ | API returns current version only |

```json
// Example Guru API Payload (POST /api/v1/cards/extended)
{
  "preferredPhrase": "Q3 Engineering Roadmap",
  "content": "<h1>Q3 Engineering Roadmap</h1><p>Migrated from Slack Canvas.</p>",
  "boardId": "string",
  "collectionId": "string",
  "verifiers": [{"type": "group", "id": "group-uuid"}],
  "verificationInterval": 90
}
```

### HTML vs. Markdown: Which Content Format to Send Guru

Guru's `POST /api/v1/cards/extended` endpoint accepts both HTML and Markdown in the `content` field. The choice has practical consequences:

**Send HTML when:**
- Your canvases contain tables. Guru's Markdown parser can mishandle complex table syntax at the edges of the 300-cell cap.
- Your canvases contain images that have been re-hosted. HTML lets you specify exact `src` attributes and `alt` text without ambiguity.
- You need precise heading levels. HTML `<h1>`, `<h2>`, `<h3>` are unambiguous; Markdown heading interpretation can vary.
- You want to sanitize and validate content before submission. HTML parsers (e.g., Python's `html.parser` or `lxml`) give you fine-grained control over malformed markup before it reaches the API.

**Send Markdown when:**
- Canvases are text-only with minimal formatting. The Markdown path is faster to implement and produces cleaner diffs if you need to audit content later.
- You want to preserve the source format for potential round-tripping back to Slack-compatible tools.

**What happens when you send Markdown with edge cases:** Guru's Markdown parser handles standard CommonMark reasonably well but has documented inconsistencies with nested lists inside tables, raw HTML blocks embedded in Markdown, and non-standard link syntax. If you are converting at scale and cannot manually inspect every card, convert to HTML and sanitize before submission. Unclosed tags, unsupported inline styles (e.g., `style="color:red"`), and `<script>` blocks will cause the endpoint to return `400 Bad Request` with a generic error body — see the error handling section below.

### How to Convert Canvas Markdown to Guru HTML

Canvas markdown supports H1, H2, and H3 headings — nothing deeper. If a canvas feels dense, split it into multiple cards rather than expecting deep heading hierarchy to organize it.

Specific conversion rules:

- **Headings**: `#`, `##`, `###` → `<h1>`, `<h2>`, `<h3>`
- **Bold/italic**: `**text**` / `_text_` → `<strong>`, `<em>`
- **Unordered lists**: `- item` → `<ul><li>item</li></ul>`
- **Ordered lists**: `1. item` → `<ol><li>item</li></ol>`
- **Code blocks**: `` ``` `` fences → `<pre><code>...</code></pre>`
- **Inline code**: `` `code` `` → `<code>code</code>`
- **Links**: `[text](url)` → `<a href="url">text</a>`
- **Tables**: standard Markdown pipe syntax → `<table><tr><td>...</td></tr></table>` (max 300 cells; verify count before conversion)
- **Checkboxes**: `- [ ]` / `- [x]` → `<ul><li>...</li></ul>` (Guru has no native checkbox element; convert to bullets)
- **Slack user mentions**: `<@U12345678>` → strip entirely or replace with plain-text display name if available from your Slack user directory
- **Slack channel links**: `<#C12345678>` → plain text channel name, optionally with a Slack deep link if the channel will still exist
- **Canvas size cap**: Slack caps each `document_content` object at 1 MiB (as detailed in our [Canvas API limits guide](https://clonepartner.com/blog/blog/how-to-import-data-into-slack-canvas-api-limits-guide)). Canvases near that limit almost certainly need to be split into multiple Guru cards.

### Guru API Error Taxonomy for Card Creation

The `POST /api/v1/cards/extended` endpoint returns these common error responses. Plan for each in your pipeline:

| HTTP status | Common cause | Remediation |
|---|---|---|
| `400 Bad Request` | Malformed HTML in `content` field (unclosed tags, unsupported inline styles, `<script>` blocks) | Run HTML through a sanitizer (e.g., `bleach` in Python, `DOMPurify` in JS) before submission |
| `400 Bad Request` | Missing required fields (`preferredPhrase`, `collectionId`, or `boardId`) | Validate payload schema before each request |
| `401 Unauthorized` | Invalid or expired API token | Rotate credentials; Guru tokens do not expire but can be revoked |
| `403 Forbidden` | Token does not have write access to the target collection | Confirm the API user is a collection admin, not read-only |
| `404 Not Found` | `collectionId`, `boardId`, or `verifiers [].id` does not exist | Validate IDs against the Guru collections and groups endpoints before batch import |
| `409 Conflict` | Duplicate `preferredPhrase` within the same collection | Append a disambiguator (e.g., channel name, date) to the card title |
| `429 Too Many Requests` | Rate limit exceeded | Back off and retry; Guru returns no `Retry-After` header — use exponential backoff starting at 1 second |

Log every non-`201` response with the full request payload. Do not silently discard failures — a failed card at import time is harder to detect than a failed card during the pre-publish review pass.

### Channels → Collections and Folders

The most natural mapping is one Slack channel → one Guru Collection, with canvases becoming cards inside it. For large workspaces this creates too many collections and makes navigation worse.

The alternative: group related channels into a single collection with folders per channel. Guru supports up to **three levels of folder nesting** inside a collection. If your Slack workspace has `#engineering`, `#eng-backend`, and `#eng-frontend`, one "Engineering" collection with folders per sub-team works well.

**A sane default:**
- **Collection = team or department.** Use when permissions and ownership line up at the team level.
- **Folder = channel or topic cluster.** Use when many canvases share the same audience.
- **Card = one answerable unit.** Do not force an entire sprawling canvas into one card if the real destination is five separate procedures.

Use a collection-per-channel model only when the Slack channel boundary is also a hard access boundary in Guru.

### Access Permissions: Private Channels and Sensitive Content

Guru collections have three permission levels: **Public** (visible to all workspace members), **Private** (visible to invited members only), and **Shared** (visible to specific groups). If a Slack canvas lived in a private channel, migrating it to a public Guru collection exposes it to the entire organization — a compliance and access control failure.

**Mapping private channels to Guru permissions via API:**

1. Check `is_private` on the Slack channel object when building your inventory.
2. For canvases from private channels, create a **Private** Guru collection with access restricted to the equivalent Guru group.
3. Create or verify the Guru group exists before card creation: `GET /api/v1/groups` to list, `POST /api/v1/groups` to create.
4. When creating the collection, set `collectionType` to `PRIVATE` and specify `memberIds` (Guru user IDs of authorized users).
5. Confirm the API token used for card creation has admin rights to the private collection — a token with only public collection access will return `403` on private collection writes.

For canvases from shared channels (channels that span multiple Slack workspaces), treat the content as sensitive by default and route it through editorial review before assigning a collection.

([help.getguru.com](https://help.getguru.com/docs/creating-managing-and-deleting-a-collection))

For another example of how Guru hierarchy translation works from a different source system, see our [Slab to Guru migration guide](https://clonepartner.com/blog/blog/slab-to-guru-migration-a-technical-guide).

## How to Handle Images and Attachments

Images embedded in Slack Canvases use Slack-hosted permalink URLs that require Slack authentication to access. If those URLs are referenced in Guru cards, they will return 403 errors — Guru users do not have Slack tokens when viewing a card. The images will break permanently once the Slack workspace or canvas is deleted.

To migrate attachments correctly:

1. **Parse the content for media.** Identify all image and file links within the canvas markdown using the `url_private` and `url_private_download` fields in the Slack file object.
2. **Download the asset.** Make a GET request to the `url_private_download` URL using your Slack bot token in the `Authorization: Bearer` header.
3. **Upload to Guru.** Make a POST call to `https://api.getguru.com/api/v1/attachments/upload`. Include the file as a parameter called `file` and upload it as `multipart/form-data`. The response returns a `link` field with the Guru-hosted URL.
4. **Rewrite the HTML.** Replace every old Slack URL in your parsed HTML with the new Guru URL before card creation.
5. **Publish the card.** Send the final HTML payload to `/api/v1/cards/extended`.

This is a per-attachment operation — there is no batch upload endpoint. A canvas with 20 images requires 21 API calls to Guru (20 for images, 1 for the card). Budget for this in your rate limit planning.

**Attachment types that do not migrate cleanly:**
- **Slack-native video recordings** (huddle recordings, clips): These are Slack-proprietary formats with no download path via the standard API. Note their existence during triage; manually download via the Slack UI if preservation is required.
- **Google Drive, Notion, or other third-party embeds**: These appear as link previews in canvases, not embedded files. The link transfers; the preview does not. Verify that destination links remain accessible from Guru.
- **Slack workflow buttons and interactive elements**: These are not supported in Guru and cannot be migrated. Strip them from the content during conversion.

For a deeper treatment of the re-hosting pattern, see our guide on [migrating images, attachments, and embeds without broken links](https://clonepartner.com/blog/blog/how-to-migrate-images-attachments-embeds-without-broken-links).

## Can Slack Canvas Comments Migrate to Guru?

Slack Canvas comments cannot be faithfully migrated to Guru inline comments.

Comments on a canvas behave like messages in threads and appear in both the Threads view and the Activity tab. In the UI, comments are section-anchored — you highlight text and add a comment to a specific passage. But the Slack API does not expose comments with their positional anchors. Comments surface as flat channel thread messages tied to the canvas file via `conversations.replies`, with no API field indicating which paragraph a comment belongs to.

Guru does have inline commenting similar to Google Docs, but the public API does not support creating inline comments programmatically. Even if it did, the positional anchor data does not exist in the Slack API response to place them correctly.

**Practical options:**

- **Drop comments entirely (recommended for most migrations).** Most Canvas comments are ephemeral discussion ("Looks good," "Updated this section"). Dropping them results in the cleanest Guru instance.
- **Append comments as a section** at the bottom of the migrated card, labeled "Migrated Comments," preserving text but not position. Extract the thread using `conversations.replies` with the canvas file's `latest_reply` timestamp as a cursor.
- **Merge meaningful comments into the card body.** Comments that change the meaning of the knowledge should be folded into the content during editorial review.
- **Archive the original canvas URL** as a link in the card so people can reference old discussion threads in Slack.

Do not attempt to reverse-engineer positional anchors. The data does not exist in the Slack API response.

## Rate Limits on Both Sides

### Slack API Rate Limits

Slack's Web API rate limits apply per method, per workspace, per app. Each method belongs to a tier:

| Tier | Rate limit | Typical methods |
|---|---|---|
| Tier 1 | ~1 req/min | Infrequent admin operations |
| Tier 2 | ~20 req/min | Some user and channel lookups |
| Tier 3 | 50+ req/min | `files.list`, `files.info`, paginating collections |
| Tier 4 | 100+ req/min | Common read operations |
| Special | Per-method | `conversations.history`, `chat.postMessage` |

`files.list` and `files.info` are both Tier 3. Exceeding limits returns HTTP `429` with a `Retry-After` header stating how many seconds to wait. Honor it exactly — continued requests during a backoff window can trigger longer lockouts.

For a workspace with 2,000 canvases, full inventory via `files.list` requires ~20 paginated calls. Content retrieval via `files.info` requires 2,000 calls. At Tier 3 minimums, content retrieval alone takes ~40 minutes if run sequentially — parallelize across multiple app tokens if timeline is a constraint.

### Guru API Rate Limits

Guru does not publish official rate limit tiers. Empirically observed safe ceiling: **200 requests per minute per API token**. The API returns HTTP `429` when throttled with no `Retry-After` header — implement exponential backoff starting at 1 second, doubling up to a 60-second ceiling.

**Practical throughput math for a 500-canvas migration:**

| Operation | Calls | Time at 200 req/min |
|---|---|---|
| Card creation (500 cards) | 500 | 2.5 min |
| Image uploads (avg 3 per canvas, 300 canvases with images) | 900 | 4.5 min |
| Group/collection validation (pre-flight) | ~50 | <1 min |
| **Total Guru API time** | **~1,450** | **~8 min** |

The API work takes under 10 minutes. The editorial triage spreadsheet will have taken two weeks.

## Rollback Strategy: What to Do When a Batch Goes Wrong

If you publish 200 cards with wrong verifiers, broken HTML, or incorrect collection assignments, you need a remediation path. Guru does not have a native "undo import" feature, but the API supports bulk updates.

**Before publishing any batch:** Tag all migrated cards with a migration-specific tag during creation (e.g., `migration-2025-q2`). This makes them discoverable via the List Cards endpoint for audit or rollback.

**To update verifiers in bulk:**
```bash
# 1. List all cards with the migration tag
GET /api/v1/cards?q=tag:"migration-2025-q2"

# 2. For each card, update the verifier
PUT /api/v1/cards/{cardId}
{
  "verifiers": [{"type": "group", "id": "correct-group-uuid"}]
}
```

**To delete a bad batch:**
```bash
DELETE /api/v1/cards/{cardId}
```

There is no batch delete endpoint. For 200 cards, deletion is 200 sequential API calls. At 200 req/min, that is about 1 minute of API time.

**Dry-run strategy before full migration:** Publish a pilot batch of 10–20 cards covering your most complex content types (image-heavy, table-heavy, long content, private channel source). Validate rendering, verifier assignment, permission inheritance, and search visibility before scaling to the full set. Flag these pilot cards with a separate tag (e.g., `migration-pilot`) so they are easy to identify and clean up.

## Post-Migration: Validation and the Ongoing Verification Burden

### Validating the migration

Guru Query Language lets you audit cards through the List Cards endpoint using filters on verification state, interval, verifier, attachments, and folder membership. ([developer.getguru.com](https://developer.getguru.com/docs/guru-query-language))

```text
verificationState != trusted
hasFileAttachment
verInterval = 90
tag = "migration-2025-q2"
```

A practical validation pass checks:

- Card count against the number of canvases that passed triage
- Every card has an assigned verifier and an intentional interval
- No image or file URLs still point to Slack private assets (search for `slack.com` in card content via the API)
- Table-heavy cards render cleanly and did not silently lose cells
- Channel-to-folder or channel-to-collection mapping matches your permission design
- Private channel content landed in private collections, not public ones
- Pilot batch cards are identified and either promoted or deleted before announcing go-live

For a full validation checklist, see our [zero-downtime migration plan](https://clonepartner.com/blog/blog/the-ultimate-knowledge-base-migration-checklist-a-zero-downtime-plan).

### Bulk Import Alternatives to the API

The API is the only path for migrations over ~50 canvases that require programmatic verifier assignment, custom metadata, and image re-hosting. For completeness, two alternatives exist:

- **Guru's CSV import**: Supports bulk card creation with title, content, collection, and folder fields. Does not support setting verifiers or verification intervals per card — all imported cards receive the importing user as verifier and the workspace default interval. Not suitable for large migrations where ownership mapping matters.
- **Guru browser extension**: Allows manual capture of web content one card at a time. Not suitable for batch migration.

For Slack Canvas migration at any meaningful scale, the API is the correct path.

### The Verification Burden Is Ongoing

This is the part most migration plans underestimate. Assigning verifiers and intervals during migration is step one. Guru's verification system runs continuously after migration — that is by design.

When a card becomes unverified, it is flagged in the verifier's task queue. The card remains searchable and visible, but users can see that it has not been recently reviewed. If your migration created 400 cards with 30-day verification intervals, someone is reviewing roughly 13 cards per day, every day, indefinitely.

This is a feature, not a bug — it is exactly why organizations choose Guru. But if the team is not briefed on the incoming verification workload, trust scores will crater within a month of go-live.

To mitigate this, stagger the verification intervals during the data mapping phase:

| Content type | Recommended interval |
|---|---|
| Critical architecture docs, security policies | 30 days |
| Onboarding checklists, active SOPs | 90 days |
| Standard operating procedures, playbooks | 90–180 days |
| Historical reference, archived project docs | 365 days |
| Legal/compliance content (rarely changes) | Up to 3,650 days (10 years) |

> [!WARNING]
> Before migrating, confirm: Who handles verification when a verifier leaves the company? Are the intervals realistic for the team's capacity? Has leadership signed off on the ongoing review commitment? Are private channel canvases mapped to appropriately restricted collections? Do team leads understand that verification requests will start arriving within 30 days of go-live? If the answers are not clear, the migration may technically succeed while operationally failing.

## When to DIY and When to Bring In Help

If your workspace has fewer than 50 canvases and a clear owner for each one, you can handle this with a weekend of scripting and a shared spreadsheet. The editorial decisions fit in a single meeting.

For larger workspaces — hundreds of canvases, multiple teams, ambiguous ownership, private channel content with compliance implications — the editorial coordination becomes a project in itself. The scripting is the easy part. If you need a team that has handled this work across [1,500+ knowledge base migrations](https://clonepartner.com/blog/blog/how-to-choose-an-enterprise-knowledge-base-migration-service) and can run both the technical pipeline and the editorial triage without stalling your timeline, that is what we do.

> Need help migrating Slack Canvas to Guru — including the verification planning that makes or breaks adoption? Book a 30-minute call with our migration engineers.
>
> [Talk to us](https://clonepartner.com/talk-to-us?duration=30&utm_source=blog&utm_medium=button&utm_campaign=demo_bookings&utm_content=cta_click&utm_term=demo_button_click)

## Frequently asked questions

### Is there a native Slack Canvas to Guru migration tool?

No. There is no built-in import path from Slack Canvas to Guru. You need a custom API-driven pipeline using Slack's files.list (filtered to type canvas) on the extract side and Guru's POST /api/v1/cards/extended on the load side, plus manual editorial decisions for verifier assignments.

### How do I list all Slack Canvases via the API?

Use the files.list method with types=canvas. There is no dedicated canvases.list endpoint. Paginate at 100–200 results per page. The method is rate-limited at Tier 3 (50+ requests per minute per workspace per app).

### Can Slack Canvas comments migrate to Guru inline comments?

Not faithfully. Canvas comments are section-anchored in the UI, but the Slack API exposes them as flat channel thread messages with no positional data. Guru supports inline comments but has no public API for creating them programmatically. Append important comments as a text section at the bottom of the card, or drop them.

### What is Guru's verification interval and can you disable it?

Every Guru card has a verification interval — the frequency in days at which it must be re-reviewed. The default is 30 days. Verification cannot be turned off. The longest workaround is setting a custom verification date up to 10 years in the future.

### Does Guru accept Markdown or only HTML via the card creation API?

Guru's POST /api/v1/cards/extended endpoint accepts both HTML and Markdown for the content field. Card bodies are stored as HTML. Converting to clean HTML before import gives you tighter control over tables, images, and link rewrites than relying on Guru's markdown parser.
