Slab to Guru Migration: A Technical Guide
No native Slab-to-Guru migration path exists. This guide covers API-driven methods, Quill Delta to HTML conversion, topic-to-collection mapping, and validation.
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
There is no native, one-click migration path from Slab to Guru. Guru does list a "Slab" import option under its content migration settings, but it works by ingesting Markdown files — the same Markdown ZIP you manually export from Slab's admin panel. For a small workspace with a few dozen posts, that manual path works. For anything with hundreds of posts, nested topics, embedded integrations, and verification metadata, the manual path breaks down and you need an API-driven pipeline or a managed migration service.
Slab-to-Guru migration is the translation of Slab's Topics + Posts model into Guru's Collections + Folders + Cards model while preserving structure, permissions, internal links, attachments, and verification ownership. The translation work matters more than the file export.
This guide covers the architectural differences between Slab and Guru, every viable migration method with honest trade-offs, the exact API constraints that break naive scripts, data mapping from Slab's topic-based model to Guru's collection-and-card hierarchy, and a validation strategy for a production-grade migration.
Slab vs. Guru: Architecture Differences That Drive Migration Complexity
Slab is an internal knowledge base organized around Posts and Topics. Slab organizes content by hierarchical topics you can easily structure and everyone can view. You can also apply many topics to one post (like labels). Posts are the atomic content unit. Slab stores post content in Quill Delta format, a JSON-based rich text representation. The platform supports verification workflows, real-time collaborative editing, and unified search across integrated tools like GitHub and Jira.
Guru is a knowledge-sharing platform built around a Collection → Folder → Card hierarchy. Each Card holds one specific piece of information, designed to be searchable, verifiable, and easy to update. You can nest folders up to three levels, and each can contain Guru Cards, additional folders, or both. Collections are the top-level organizational structure in Guru. Collections manage access, permissions, and content distribution across user groups. Guru focuses heavily on in-context delivery via browser extensions and Slack, and enforces strict verification workflows where subject matter experts periodically review cards to prevent knowledge rot.
The structural mismatch is the core migration challenge:
| Concept | Slab | Guru |
|---|---|---|
| Content unit | Post (Quill Delta) | Card (HTML) |
| Organization | Topics (many-to-many labels, hierarchical) | Collections → Folders → Cards (one primary collection per card) |
| Content format | Quill Delta internally, exports as Markdown | HTML in editor and API |
| Verification | Per-post verification with owner | Per-card verification with verifier and interval |
| Access control | Topic-level (public, secret, restricted) | Collection-level and folder-level groups |
| Tagging | Topics serve as tags | Dedicated tag system separate from hierarchy |
| Nesting depth | Topics can nest multiple levels deep | Folders nest up to 3 levels within collections |
The many-to-many relationship between Slab Posts and Topics does not translate cleanly to Guru's one-card-one-collection model. A Slab post tagged with "Engineering," "Onboarding," and "Security" has to live in one Guru collection — the rest become tags or Board placements.
Companies typically move from Slab to Guru for a few specific reasons:
- Contextual delivery: Support and sales teams need answers injected directly into Zendesk, Salesforce, or Slack, rather than opening a separate wiki tab.
- Verification workflows: Guru's "trust but verify" model forces periodic review, preventing knowledge rot.
- AI-driven search: Guru's AI capabilities are optimized for fast-paced operational teams that need instant answers.
Public Cards sunset. Guru's December 2025 release notes indicate that new Public Cards can no longer be created. If your Slab instance includes publicly accessible content, test fit first — Guru may not support your public knowledge sharing needs going forward.
Migration Approaches: Every Viable Method Compared
1. Manual Export/Import (Markdown-Based)
How it works:
- In Slab, go to Team Settings → Import & Export.
- Select the file type (Markdown or Docx) and which kinds of topics to download. Click Download. After clicking the download button, an email will be sent with the download link.
- Slab admins can download all published content from Team Settings → Import & Export. The export produces a ZIP archive containing individual files — one per post — in either Markdown or Docx format.
- In Guru, go to Manage → Collection Settings → Import Content.
- If you're importing content from platforms such as Bloomfire, Jive, Intercom, Tettra, ClickUp, WordPress, or Document360 and don't see your source listed in Guru's import tool: Markdown exports can be imported using the Slab option.
- Upload the Markdown files. Guru creates one Card per file.
When to use it: Workspaces under ~50 posts with mostly text content and simple formatting.
Pros:
- Zero engineering effort
- No API access required (works on all Slab plans)
- Fast for tiny workspaces
Cons:
- Topic hierarchy is lost — you get a flat set of Cards in a single collection
- Internal links between posts break
- Embedded integrations (GitHub, Figma, Loom) export as bare URLs or are dropped
- The secret topic trap: An admin can only export content they have permission to view. If engineers created secret topics and did not invite the administrator, those documents silently fail to export.
- Verification metadata (owner, interval, last verified date) is not preserved
- No way to batch-assign Cards to specific Collections
Complexity: Low Scalability: Small workspaces only
2. API-Based Migration (Slab GraphQL → Guru REST API)
How it works:
- This project uses the Slab GraphQL API. Extract posts, topics, and metadata via Slab's GraphQL endpoint.
- Key operations: query GetPost — Fetch post content by ID; query SearchPosts — Search posts across workspace (cursor-based pagination); query GetTopicPosts — List posts in a specific topic; query GetOrganizationPosts — List all posts in the organization.
- Transform Quill Delta or Markdown content into HTML for Guru's Card format.
- Guru's API follows the REST protocol, using JSON as the data format. The root URL for all endpoints is https://api.getguru.com/api/v1/.
- Create Collections in Guru (mapped from Slab Topics), then create Folders, then create Cards with HTML content.
- Rebuild internal links by maintaining an ID mapping table (Slab post ID → Guru Card ID).
When to use it: Workspaces with 50–5,000+ posts, complex topic structures, or any requirement to preserve metadata and hierarchy.
Pros:
- Full control over data transformation
- Can preserve topic-to-collection mapping
- Can rewrite internal links programmatically
- Can set verification metadata on Guru Cards via API
- Handles attachments through download-and-reupload
Cons:
- The Business plan is $12.50 per user per month billed annually and adds the full AI suite (AI Ask, AI Predict, AI Autofix), SAML SSO, SCIM, and the GraphQL API. Slab's API requires Business or Enterprise plan.
- Quill Delta → HTML conversion requires a dedicated parser
- Significant engineering effort — complexity scales with post count, embed diversity, and hierarchy depth (see conditional estimates below)
- API rate limits on both sides require defensive retry logic
Estimated engineering effort by workspace characteristics:
| Workspace Profile | Post Count | Conditions | Estimated Effort |
|---|---|---|---|
| Simple | 50–200 | Text-only posts, flat topics, no embeds | 30–50 hours |
| Moderate | 200–1,000 | Mixed content, 2–3 levels of topic nesting, some embeds | 60–100 hours |
| Complex | 1,000+ | Heavy embeds, deep nesting, multi-topic posts, secret topics, images on CDN | 100–160 hours |
The biggest variable is the number of distinct embed types and the depth of internal cross-linking. A workspace with 500 text-only posts migrates faster than one with 200 posts containing GitHub snippets, Figma frames, and Loom videos.
Complexity: High Scalability: Enterprise-grade
3. Third-Party Migration Tools/Services
How it works: Off-the-shelf software designed to map fields and move data between platforms.
In the official docs reviewed for this guide, there is no dedicated vendor-documented one-click Slab-to-Guru migrator beyond Guru's own import options. Most third-party tools still wrap exports, imports, or APIs. Evaluate them on field mapping, link repair, permission handling, and QA reporting — not on connector labels.
When to use it: When you have budget but lack internal engineering resources, and only if the vendor can demonstrate a real mapping plan.
Pros:
- Faster than building from scratch
- Less internal coding
Cons:
- Most generic tools fail at resolving internal wiki links or handling proprietary formatting
- Opaque transforms with limited control
- Generic tools often treat knowledge bases like CRMs, resulting in poorly formatted cards
Complexity: Low to Medium Scalability: Vendor dependent
4. Custom ETL Pipeline
How it works:
- Build a dedicated extraction layer that reads all Slab data via GraphQL API (posts, topics, users, attachments).
- Store extracted data in an intermediate format (JSON or database).
- Apply transformations: Quill Delta → HTML, topic mapping → collection assignment, internal link rewriting.
- Load into Guru via REST API with retry logic and rate-limit handling.
- Run validation passes to confirm record counts and content integrity.
When to use it: Large workspaces (1,000+ posts), multiple Slab workspaces merging into one Guru instance, or when you need audit trails and rollback capability.
Pros:
- Full pipeline observability (logging, checkpoints, rollback)
- Handles deduplication and data cleaning in the transform layer
- Can be re-run for incremental updates
- Supports parallel processing with rate-limit awareness
Cons:
- Highest engineering investment
- Requires maintaining the pipeline code post-migration
- Over-engineered for small workspaces
Complexity: High Scalability: Enterprise
5. Middleware / Integration Platforms (Zapier, Make, Workato)
How it works: Setting up workflows that trigger on new or updated Slab posts and create corresponding Guru Cards.
If your team uses Zapier, you can build automations (Zaps) to create Guru Cards based on actions in other apps. Example: Trigger a new Guru Card when a new Google Doc is created.
When to use it: Only for ongoing synchronization during a phased rollout or short coexistence window. Never for historical bulk migration.
Pros:
- No-code / low-code setup
- Good for incremental, ongoing sync
- Built-in error notifications
Cons:
- Not practical for bulk historical migration (rate limits, execution time limits)
- Limited content transformation capability
- Cannot handle complex hierarchy mapping
- Zapier's task limits add up fast on large workspaces
- Poor deep-link handling
Complexity: Low to Medium Scalability: Small ongoing syncs only
Migration Approach Comparison
| Approach | Complexity | Best For | Preserves Hierarchy | Preserves Links | Bulk Scale |
|---|---|---|---|---|---|
| Manual Export/Import | Low | <50 posts, simple content | ❌ | ❌ | ❌ |
| API-Based (Custom) | High | 50–5,000 posts, technical teams | ✅ | ✅ | ✅ |
| Third-Party Tools | Low–Med | Low bandwidth, budget available | Varies | Varies | Varies |
| Custom ETL Pipeline | High | 1,000+ posts, multi-workspace | ✅ | ✅ | ✅ |
| Middleware (Zapier/Make) | Low–Med | Ongoing sync, small batches | ❌ | ❌ | ❌ |
| Managed Service | Low (your side) | Any size, tight timelines | ✅ | ✅ | ✅ |
Recommendations by scenario:
- Small team, <50 posts, no dev resources: Manual Markdown export → Guru import. Accept the hierarchy loss and fix manually.
- Mid-size team, 50–500 posts, some dev bandwidth: API-based migration with a focused script. Budget 1–2 weeks of engineering time.
- Enterprise, 500+ posts, tight deadline: Managed migration service or dedicated ETL pipeline. The cost of engineering time and risk of data loss often exceeds the service fee.
- Ongoing sync (Slab and Guru running in parallel): Zapier/Make for incremental new content, paired with a one-time bulk migration for historical data.
Guru's own guidance says to connect a Source when the original system should remain the system of record, but Slab is not listed among Guru's built-in Sources. For Slab specifically, you need a real migration, not just a source connection.
Pre-Migration Planning
Data Audit Checklist
Before writing a single line of migration code, inventory what you actually have in Slab:
- Published posts: Count by topic. Identify orphaned posts (no topic assigned).
- Draft posts: Decide whether to migrate drafts or archive them. Slab's export only includes published content.
- Secret topics: The secret topic trap: An admin can only export content they have permission to view. Ensure the migration account has access to all secret topics.
- Topic hierarchy and depth: Identify any nesting deeper than three levels. Guru folders cap at three nested levels, so deeper Slab trees need flattening.
- Multi-topic posts: Flag every post that belongs to multiple topics. These require a primary topic decision.
- Embedded integrations: Posts with GitHub, Figma, Loom, or Google Drive embeds. These will not render in Guru — plan for manual replacement or accept bare URLs.
- Attachments and images: Inline images in Slab are hosted on Slab's CDN. They must be downloaded and re-uploaded to Guru or hosted externally.
- Verification metadata: Which posts have active verification owners and intervals? You'll want to re-establish these in Guru.
- Internal links: Map all cross-post references. These break on migration unless rewritten.
- Content size distribution: Identify posts exceeding ~50,000 characters of HTML (Guru's approximate card limit). In practice, fewer than 5% of Slab posts typically exceed this threshold, but long-form runbooks and onboarding guides are the usual offenders. Pre-flag these for splitting.
Define Migration Scope
Not everything in Slab needs to go to Guru. Ask:
- Are there stale or outdated posts that should be archived instead of migrated? Use Slab's analytics to find posts with zero views in the last 12 months.
- Are there topics that map better to a different tool (e.g., engineering runbooks that belong in a Git repo)?
- Do you need historical version data, or just the current published version? Slab does not export version history through any method — manual or API.
- Does any content need to remain externally accessible after cutover? New Public Cards in Guru are no longer supported.
Migration Strategy
| Strategy | When to Use | Risk Level |
|---|---|---|
| Big bang | Small workspace, short downtime window acceptable | Medium |
| Phased (topic by topic) | Large workspace, need to validate per-team | Low |
| Incremental (sync new content while migrating history) | Parallel run period required, API/ETL rerun capability available | Medium |
For most teams, a phased approach — migrating one Slab topic at a time into a corresponding Guru Collection — gives you the best risk profile. You validate each batch before moving on, and you can pause if issues surface.
For knowledge bases specifically, a "big bang" cutover over a weekend can also work well to prevent users from editing data in two places. The right call depends on your workspace size and team tolerance for parallel systems.
Data Model and Object Mapping
This is where the migration either preserves your team's information architecture or destroys it. Slab and Guru have fundamentally different organizational models.
Structural Mapping
| Slab Object | Guru Equivalent | Notes |
|---|---|---|
| Post | Card | 1:1 mapping. Content format changes from Quill Delta/Markdown to HTML. |
| Topic (top-level) | Collection | Each major Slab topic becomes a Guru Collection. |
| Topic (nested/child) | Folder | Sub-topics map to Folders within a Collection. Max 3 levels. |
| Topic (as label) | Tag | When a post has multiple topics, secondary topics become Guru Tags. |
| Post verification | Card verification | Map verification owner → Guru verifier; set verification interval. |
| Post comments | Card comments | Guru supports comments on Cards; migrate via API if needed. |
| Post author | Card owner / metadata | Map via email address to preserve authorship. |
| Secret topic | Collection with restricted group | Guru's group-based permissions replace Slab's secret topic model (see permission mapping below). |
| Embedded integration | Inline URL or unsupported | GitHub/Figma/Loom embeds have no Guru equivalent. |
Permission Mapping: Slab Visibility → Guru Access Control
Slab's access model is topic-centric with three visibility tiers. Guru's is group-based at the Collection and Folder level. The mapping:
| Slab Visibility | Guru Equivalent | Implementation |
|---|---|---|
| Public topic (visible to all workspace members) | Collection with "All Members" group | Create the Collection and assign the default "All Members" group as readers. |
| Restricted topic (visible to invited members only) | Collection or Folder with specific group(s) | Create a Guru Group matching the Slab topic's member list. Assign that group to the Collection. |
| Secret topic (hidden from non-members, not discoverable) | Collection with restricted group, removed from "All Members" | Create a dedicated Guru Group. Assign only that group. Ensure "All Members" does not have access. Note: Guru does not have a true "hidden" mode — users without access simply won't see the Collection, but its existence is not actively concealed in the same way Slab hides secret topics. |
Apply permissions at the Collection or folder level to ensure the right people have the right access. Folder permissions are additive, giving you flexible control without overcomplicating things.
Key difference: Slab's permission inheritance flows from topic → post. Guru's flows from Collection → Folder → Card, with folder permissions being additive (they can grant access but not restrict it beyond the Collection level). If a Slab secret topic contains posts that also belong to a public topic, the migration must resolve the conflict — the Card can only live in one Collection with one permission model.
The Many-to-Many Topic Problem
Slab organizes and structures content by hierarchical topics, and allows users to add multiple topics (labels) to one post. Guru requires each Card to belong to exactly one Collection. A Slab post tagged with three topics forces a decision:
- Pick a primary topic → That becomes the Card's Collection.
- Secondary topics → Convert to Guru Tags.
- Board placements → Optionally add the Card to Boards for cross-functional visibility.
Define this mapping rule before migration. Without it, your script will either duplicate Cards across Collections (creating sync nightmares) or silently drop topic associations.
Decision heuristic for primary topic selection:
- If one topic is a parent and others are children, choose the most specific (deepest) topic as primary.
- If topics are unrelated (e.g., "Engineering" and "Onboarding"), choose the topic where the content is most frequently accessed (use Slab analytics if available).
- If access control differs between topics, choose the topic whose visibility matches the content's sensitivity.
Structural Constraints That Force Compromises
Two Guru-side constraints drive most mapping decisions:
-
Folder depth limit. Guru folders support only three nested levels. Slab topics can nest deeper. Any hierarchy beyond three levels needs to be flattened — use a combination of folders, tags, and card headings to preserve the original structure's intent.
-
Card-sized content. Guru is designed for bite-sized, searchable Cards — not 3,000-word documents. Break long documents into multiple Cards using H1 or H2 headers to make knowledge easier to find. Consider splitting long Slab posts at H2 boundaries during transformation.
Verification defaults changed. Guru's documentation indicates that Collections created after January 2026 default cards to "Does not expire." Do not rely on defaults if you want periodic re-verification — set verification intervals explicitly via the API during migration.
Migration Architecture: Extract → Transform → Load
Extract: Getting Data Out of Slab
Option A: Manual export — Markdown ZIP from Team Settings. No API needed, works on all plans. Loses metadata, hierarchy, and verification data.
Option B: GraphQL API — Available on Business and Enterprise plans only. Available to Enterprise customers. Contact us if you would like to upgrade. Slab's API is in GraphQL.
Sample extraction query:
query GetOrganizationPosts($cursor: String) {
organization {
posts(first: 50, after: $cursor) {
edges {
node {
id
title
content
insertedAt
updatedAt
topics {
id
name
parentTopic {
id
name
}
}
owner {
id
name
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}No rate limits or pagination parameters are publicly documented by Slab. Treat pagination as unconfirmed and build defensive retry logic accordingly.
Slab API access requires Business or Enterprise plan. Slab offers a GraphQL API, but it's only available on Business or Enterprise plans. If you're on the free or Startup plan, your only extraction option is the manual Markdown export.
Transform: Content Conversion
The core transformation challenge is Quill Delta → HTML. Slab stores post content internally as Quill Delta JSON. When you query the API, you get either the Delta or Markdown representation depending on the field you request.
For Guru, you need clean HTML. The transformation pipeline:
- Parse Quill Delta JSON into an AST (abstract syntax tree).
- Convert each Delta operation to its HTML equivalent.
- Handle edge cases:
- Nested lists (Delta uses indent levels, not
<ul>nesting) - Code blocks with language annotations
- Inline embeds (Slab-specific embed types like GitHub/Figma)
- Tables (Slab uses a custom table format)
- Images (download from Slab CDN, re-upload to Guru or external host)
- Nested lists (Delta uses indent levels, not
- Rewrite internal links: replace
https://{team}.slab.com/posts/{slug}with Guru Card URLs.
If you export Markdown instead of using the API, the transformation is simpler (Markdown → HTML), but you lose metadata and the Markdown export has its own formatting gaps.
Quill Delta Operation → HTML Reference
Quill Delta represents content as an array of insert operations with optional attributes. Below is a mapping of the most common operations encountered in Slab exports to their HTML equivalents:
| Delta Operation | Attributes | HTML Output | Notes |
|---|---|---|---|
insert: "text" |
bold: true |
<strong>text</strong> |
|
insert: "text" |
italic: true |
<em>text</em> |
|
insert: "text" |
link: "url" |
<a href="url">text</a> |
Check for internal Slab links here |
insert: "text" |
code: true |
<code>text</code> |
Inline code |
insert: "text" |
strike: true |
<s>text</s> |
|
insert: "\n" |
header: 1 |
Wrap preceding line in <h1> |
Delta signals headers on the trailing newline |
insert: "\n" |
header: 2 |
Wrap preceding line in <h2> |
Same for H2–H6 |
insert: "\n" |
list: "ordered" |
Wrap in <ol><li> |
|
insert: "\n" |
list: "bullet" |
Wrap in <ul><li> |
|
insert: "\n" |
list: "checked" or list: "unchecked" |
<ul><li> with checkbox |
Slab uses task lists |
insert: "\n" |
indent: N |
Nest <li> inside parent <ul>/<ol> |
Delta uses flat indent levels (0, 1, 2…); you must reconstruct nested <ul> trees |
insert: "\n" |
blockquote: true |
<blockquote> |
|
insert: "\n" |
code-block: "python" |
<pre><code class="language-python"> |
Language tag may be absent |
insert: {image: "url"} |
— | <img src="url"> |
URL points to Slab CDN; must download and re-host |
insert: {video: "url"} |
— | <a href="url">Video</a> |
No native video embed in Guru |
insert: {slab-embed: {...}} |
type: "github", type: "figma", etc. |
<a href="url"> [Type] Embed</a> |
Slab-proprietary; no Guru equivalent. Convert to descriptive hyperlink |
insert: {table: [...]} |
— | <table> with <tr>/<td> |
Slab's table Delta format is non-standard; requires custom parsing |
insert: {divider: true} |
— | <hr> |
Critical parsing pitfalls:
- Header detection is retroactive. In Quill Delta, the
headerattribute appears on the\nafter the header text, not before it. Your parser must buffer text and apply formatting when the newline operation arrives. - Nested list reconstruction. Delta represents indent levels as flat integers (
indent: 0,indent: 1,indent: 2). You must track indent state and open/close<ul>or<ol>tags accordingly. Off-by-one errors here produce malformed HTML that renders incorrectly in Guru. - Consecutive formatting. When multiple attributes apply to the same text (
bold+italic+link), nest the HTML tags consistently:<a href="..."><strong><em>text</em></strong></a>.
For Python implementations, the quill-delta package provides Delta parsing primitives, but does not handle Slab-specific embed types. You will need custom handlers for slab-embed operations and Slab's table format. For JavaScript, quill-delta-to-html is more mature but similarly requires custom converters for Slab-proprietary operations.
Load: Writing Data to Guru
Guru is a knowledge sharing platform that provides a REST API for managing teams, cards, collections and related resources. The REST API base URL is https://api.getguru.com/api/v1/.
Guru uses HTTP Basic Auth (user email + API token). Slab uses bot user API tokens with GraphQL. Slab uses bot user API tokens for API access. Make sure your migration script handles both authentication schemes correctly.
Loading sequence (order matters for dependencies):
- Create Collections — One per primary Slab topic.
- Create Folders — One per sub-topic, nested within the parent Collection.
- Create Cards — One per Slab post, assigned to the correct Collection and Folder.
- Set Tags — Apply secondary topic names as Guru Tags.
- Set Verification — Assign verifier and interval per Card.
- Rewrite Links — Update all internal Card references using the ID mapping table.
Guru does not offer a documented bulk import API endpoint. Cards must be created individually via POST /cards. For large migrations, this means the load phase is bounded by per-card API calls. At a conservative 1 request/second, a 1,000-card migration takes approximately 17 minutes of pure API time (not counting retries, folder creation, or link rewriting passes). Parallelizing across 3–4 concurrent requests can reduce this, but monitor for 429 responses and back off accordingly.
Sample Card creation via Guru API:
curl -X POST https://api.getguru.com/api/v1/cards \
-u "user@example.com:API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"preferredPhrase": "Post Title from Slab",
"content": "<p>HTML content converted from Slab</p>",
"collection": {
"id": "COLLECTION_UUID"
},
"verifiers": [{
"type": "user",
"user": { "email": "[email protected]" }
}],
"verificationInterval": 90
}'429 Too Many Requests — Guru enforces a rate limit per account. If you receive this response, back‑off for a few seconds before retrying. Specific numeric limits are not publicly documented in official sources. Contact Guru support for enterprise rate limit details.
Build with backpressure in mind. Since Guru doesn't publish specific rate limits, start conservatively (1–2 requests/second) and increase until you see 429s. Implement exponential backoff with jitter. Log every 429 response with timestamp and endpoint so you can calculate the effective limit for your account tier.
Step-by-Step Migration Process
Step 1: Audit and Access Setup
- Ensure the migration account has access to all Slab topics, including secret ones.
- Generate a Slab API token (Settings → API, Business/Enterprise only).
- Generate a Guru API user token (Apps & Integrations → API Access tab). User tokens allow both read and write access to Guru's API based on the permissions of the user generating the token.
- Export a full Markdown/ZIP backup from Slab and store it in cold storage before touching the API.
Step 2: Extract All Content from Slab
- Query
GetOrganizationPostswith cursor-based pagination to pull all posts. - For each post, extract: ID, title, content (Quill Delta or Markdown), topics, owner, creation date, update date.
- Download all inline images and attachments. Slab API returns signed URLs for attachments — these expire. Your script must download files immediately and store them locally with a mapping of original URL → local path.
Step 3: Build the Topic → Collection Map
- List all Slab topics and their hierarchy.
- Decide which top-level topics become Guru Collections.
- Decide which sub-topics become Guru Folders (respecting the 3-level nesting limit).
- For posts with multiple topics, define the "primary" topic rule (see decision heuristic above).
- Generate a Topic-to-Collection/Folder ID mapping dictionary.
Step 4: Transform Content
- Convert Quill Delta → HTML using the operation mapping table above (or Markdown → HTML if using export files).
- Re-upload images to Guru (or a CDN) and replace URLs in HTML.
- Build a link mapping table:
{slab_post_id: guru_card_id}— populated as Cards are created. - Split any posts that exceed Guru's card size limit (~50,000 characters of HTML).
Step 5: Load into Guru
- Create Collections via API.
- Create Folders within each Collection.
- Create Cards with HTML content, assigning each to its Collection and Folder.
- Apply Tags for secondary topics.
- Set verification owners and intervals.
- Store the new Guru Card ID for every successfully created Card.
Step 6: Rewrite Internal Links (The Second Pass)
- After all Cards are created, iterate through every Card.
- Parse the HTML to find all
hreflinks pointing toslab.com/posts/{id}or{team}.slab.com/posts/{slug}. - Replace Slab post URLs with Guru Card URLs using the mapping table.
- Update each Card via the Guru API (
PUT /cards/{cardId}).
Note on multi-hop links: If a Slab post links to another post which links to a third post, a single-pass rewrite catches all hops because every Card's content is scanned independently. The risk arises if some posts failed to migrate — those become dead links. Cross-reference your link mapping table against missing entries before running the rewrite pass.
Step 7: Validate
- Compare total post count in Slab vs. Card count in Guru.
- Spot-check 10–15% of Cards for content accuracy (formatting, images, links).
- Verify that all Collections and Folders match the planned hierarchy.
- Confirm verification owners are correctly assigned.
- Have 2–3 power users per team review their migrated Collections with a checklist.
Edge Cases and Challenges
Embedded Third-Party Content
Slab embeds from GitHub, Figma, Loom, Google Drive, and other integrations render inline in Slab but have no equivalent in Guru. During migration, these either export as bare URLs (best case) or are silently dropped (worst case).
Mitigation: Pre-scan posts for embed patterns (look for slab-embed operations in Quill Delta or <iframe> / Slab-specific embed markers). Convert to clickable hyperlinks with descriptive text (e.g., " [GitHub PR #1234] link" rather than a bare URL).
Duplicate Posts
Slab allows posts with identical titles in different topics. Guru requires unique Card titles within certain contexts. Before migrating, deduplicate by comparing titles and content hashes. If duplicates serve different audiences, append the topic name to the Card title during import. Merging duplicates in Slab is cheaper than cleaning up duplicate Cards in Guru.
Long-Form Posts vs. Card-Sized Content
Slab posts can be arbitrarily long. Guru Cards have a maximum content limit (approximately 50,000 characters of HTML). When splitting, create a "parent" Card with a table of contents linking to the child Cards, and apply a shared tag so users can find all parts.
Attachments and Image Re-Hosting
Images are included via URL only, not as embedded files. If your Slab images are hosted on Slab's CDN, they will stop working after you close the Slab account. Download and re-host all images before decommissioning Slab.
Implementation: During extraction, scan each post's Quill Delta for insert: {image: "url"} operations. Download each image URL to local storage. After creating the Guru Card, upload images to your chosen host (S3 bucket, Cloudflare R2, or Guru's own attachment mechanism) and update the <img src> URLs in the Card HTML.
Rollback at Scale
Deleting Cards via Guru's API is subject to the same rate limits as creation. If you need to roll back a batch of 500 Cards, your deletion script will take as long as the creation did. Plan for this by tracking Card IDs per batch in a checkpoint file, so you can target specific batches for rollback without touching successfully migrated content.
API Failures and Retries
Always implement retry logic for 429 Too Many Requests and 5xx Server Errors. Use exponential backoff with jitter. Log every failed request with the Slab post ID so you can identify and re-process gaps.
Limitations and Constraints
Slab-Side Limitations
- API gated by plan. GraphQL API is only available on Business ($12.50/user/mo billed annually) or Enterprise plans. Free and Startup plans offer no programmatic access.
- No version history export. Only the current published version of each post is accessible via any method.
- Secret topic visibility. The API only returns content the authenticated user can access.
- No bulk metadata export. You cannot export verification history, analytics, or comment threads through the manual export.
Guru-Side Limitations
- One Collection per Card. Cannot replicate Slab's many-to-many topic model natively. Must rely on Tags and Boards.
- Folder depth capped at three levels. Deeper Slab hierarchies require flattening.
- No bulk Card creation API. Cards must be created one at a time via
POST /cards. - Rate limits undocumented. Rate limits: Guru enforces rate limits on API requests. Specific numeric limits are not publicly documented in official sources.
- Card exports are not customizable. Guru cannot add, modify, or remove export fields.
- Guru does not extract text from PDFs.
- No native Quill Delta support. All content must be converted to HTML before import.
- Public Cards sunset. New Public Cards can no longer be created as of December 2025.
Potential Data Loss Scenarios
| Scenario | What Gets Lost | Mitigation |
|---|---|---|
| Manual export only | Topic hierarchy, verification data, comments, drafts | Use API instead |
| Skipping access audit | Secret topic content | Grant migration account access to all topics |
| Not re-hosting images | Images break when Slab account closes | Download and re-upload all images during extraction |
| Ignoring embed content | GitHub/Figma/Loom embeds | Convert to hyperlinks pre-migration |
| Skipping second-pass link rewrite | Internal links point to dead Slab URLs | Run link rewriter after all Cards exist |
| Not mapping permissions | Secret topic content becomes visible to all | Map Slab visibility tiers to Guru Groups explicitly |
Validation and Testing
Record Count Comparison
Slab published posts: 342
Guru Cards created: 342
Mismatch: 0
Run this check for every Collection/Topic mapping, not just the total.
Field-Level Validation
For a random 10–15% sample:
- Title matches exactly
- HTML content renders correctly (check formatting, headers, lists, tables, code blocks)
- Images load from new URLs
- Internal links point to valid Guru Cards
- Tags match secondary Slab topics
- Verification owner and interval are correct
Specifically check Cards that contained complex tables, nested lists with 3+ indent levels, code blocks with language tags, or heavy image usage — these are the most likely to have transformation issues.
UAT Process
- Invite 2–3 power users per team to review their migrated Collections.
- Provide a checklist: content accuracy, link functionality, search discoverability.
- Have users attempt their daily workflows in Guru using the migrated data for one afternoon in a sandbox environment.
- Document and fix issues before full rollout.
Rollback Planning
Guru's API supports Card deletion. If a batch fails validation:
- Delete all Cards created in the failed batch (track Card IDs during creation in a checkpoint file).
- Investigate the failure.
- Re-run the batch after fixing the issue.
Keep Slab active (read-only) until validation is complete. Do not close your Slab account until you have confirmed all content is intact in Guru.
Post-Migration Tasks
Rebuild Organizational Structure
- Configure Guru Boards for cross-collection views (replacing Slab's multi-topic browsing experience).
- Set up Guru's verification schedules to match your team's content review cadence.
Rebuild Automations
If you used Zapier or other tools to push alerts to Slack when a Slab post was updated, you must rebuild these workflows for Guru. Check for any webhooks, notifications, or integrations that referenced Slab.
User Training and Onboarding
- Guru's browser extension is central to its value proposition — ensure all users install it.
- Communicate the new information architecture: which Collections replaced which Slab Topics.
- Train your team on Guru's verification workflow. A migrated Card is useless if it expires and gets ignored a month later.
- Provide a mapping reference document so users know where to find content that moved.
Monitor for Issues
- Track search analytics in Guru for the first 30 days. Low search hit rates may indicate content is miscategorized.
- Monitor for broken links or missing images reported by users.
- Check that Guru's AI features (if enabled) are indexing migrated content correctly.
Best Practices
- Back up everything before starting. Export Slab's full Markdown ZIP and store it independently. Keep it in cold storage.
- Run a test migration first. Pick one small topic (10–20 posts) and migrate it end-to-end. Validate HTML rendering thoroughly before scaling.
- Validate incrementally. Don't wait until all Cards are created to start checking. Validate each Collection batch as it completes. Validate link rewriting immediately after the second pass.
- Automate link rewriting. This is the most error-prone manual task. A simple script that replaces URLs using a lookup table saves hours of post-migration debugging.
- Plan for image re-hosting early. Download all images during the extraction phase, not as an afterthought. Once the Slab account closes, CDN-hosted images become permanently inaccessible.
- Document your mapping decisions. The topic-to-collection map, the primary topic rule, the permission mapping, and any content that was intentionally excluded — write it all down. Future you will need it.
- Set verification intervals proactively. Use the Slab document's last edited date to assign initial verification status — recently updated content can have longer intervals; stale content should be flagged for immediate review.
For more on planning a migration like this, see our knowledge base migration checklist.
Sample Data Mapping Table
| Slab Field | Guru Field | Transformation |
|---|---|---|
post.title |
card.preferredPhrase |
Direct copy. Must be unique or appended with context. |
post.content (Quill Delta) |
card.content (HTML) |
Quill Delta → HTML conversion using operation mapping. Split if exceeds ~50K chars. |
post.topics [0] (primary) |
card.collection.id |
Map via topic → collection lookup. |
post.topics [1..n] (secondary) |
card.tags [] |
Topic name → Tag. |
post.owner.email |
card.verifiers [0].user.email |
Direct mapping. |
post.insertedAt |
Not directly settable | Guru sets creation timestamp automatically. |
post.updatedAt |
Not directly settable | Guru tracks its own update timestamps. |
| Topic hierarchy (parent → child) | Collection → Folder | Parent topic = Collection; child topic = Folder. Flatten beyond 3 levels. |
| Post visibility (public/secret/restricted) | Collection group permissions | Public → All Members group; Secret → dedicated restricted group; Restricted → specific group matching invited members. |
| Inline images | Card images (re-hosted URLs) | Download → re-upload → replace URLs. |
| Internal post links | Internal Card links | URL rewrite using ID mapping table. |
Automation Script Outline (Python)
import requests
import json
import time
import os
import hashlib
# --- Config ---
SLAB_API_TOKEN = "your-slab-token"
SLAB_TEAM = "your-team"
GURU_USER = "[email protected]"
GURU_TOKEN = "your-guru-token"
GURU_BASE = "https://api.getguru.com/api/v1"
CHECKPOINT_FILE = "migration_checkpoint.json"
IMAGE_DIR = "downloaded_images"
# --- Checkpoint persistence ---
def load_checkpoint():
if os.path.exists(CHECKPOINT_FILE):
with open(CHECKPOINT_FILE) as f:
return json.load(f)
return {"id_map": {}, "last_cursor": None, "failed": []}
def save_checkpoint(state):
with open(CHECKPOINT_FILE, "w") as f:
json.dump(state, f)
# --- Step 1: Extract from Slab ---
def fetch_slab_posts(cursor=None):
query = """
query($cursor: String) {
organization {
posts(first: 50, after: $cursor) {
edges { node { id title content topics { id name } owner { email } } }
pageInfo { hasNextPage endCursor }
}
}
}
"""
resp = requests.post(
f"https://{SLAB_TEAM}.slab.com/api/v1/graphql",
json={"query": query, "variables": {"cursor": cursor}},
headers={"Authorization": f"Bearer {SLAB_API_TOKEN}"}
)
resp.raise_for_status()
return resp.json()
# --- Step 2: Transform ---
def quill_delta_to_html(delta_json):
"""
Convert Quill Delta operations to HTML.
Use quill-delta package (pip install quill-delta) for parsing,
then apply custom handlers for slab-embed, table, and image operations.
This function must handle: header retroactive detection, nested list
reconstruction from indent levels, and slab-proprietary embed types.
"""
# Implementation required - see Quill Delta operation table in guide
raise NotImplementedError("Implement with custom Slab-aware parser")
def download_image(url, post_id):
"""Download image from Slab CDN before signed URL expires."""
os.makedirs(IMAGE_DIR, exist_ok=True)
ext = url.split(".")[-1].split("?")[0][:4]
filename = f"{post_id}_{hashlib.md5(url.encode()).hexdigest()}.{ext}"
filepath = os.path.join(IMAGE_DIR, filename)
if not os.path.exists(filepath):
resp = requests.get(url, timeout=30)
resp.raise_for_status()
with open(filepath, "wb") as f:
f.write(resp.content)
return filepath
# --- Step 3: Load into Guru ---
def create_guru_card(title, html_content, collection_id, verifier_email=None,
max_retries=5):
payload = {
"preferredPhrase": title,
"content": html_content,
"collection": {"id": collection_id}
}
if verifier_email:
payload["verifiers"] = [{"type": "user",
"user": {"email": verifier_email}}]
payload["verificationInterval"] = 90
for attempt in range(max_retries):
resp = requests.post(
f"{GURU_BASE}/cards",
auth=(GURU_USER, GURU_TOKEN),
json=payload
)
if resp.status_code == 429:
wait = (2 ** attempt) + (hash(title) % 1000) / 1000 # backoff + jitter
time.sleep(wait)
continue
if resp.status_code >= 500:
time.sleep(2 ** attempt)
continue
resp.raise_for_status()
return resp.json()
raise Exception(f"Failed to create card '{title}' after {max_retries} retries")
# --- Main Pipeline ---
def migrate():
state = load_checkpoint()
id_map = state["id_map"]
cursor = state["last_cursor"]
while True:
data = fetch_slab_posts(cursor)
posts = data["data"]["organization"]["posts"]
for edge in posts["edges"]:
post = edge["node"]
if post["id"] in id_map:
continue # Already migrated
try:
html = quill_delta_to_html(post["content"])
collection_id = map_topic_to_collection(post["topics"])
verifier = post.get("owner", {}).get("email")
card = create_guru_card(post["title"], html, collection_id,
verifier)
id_map[post["id"]] = card["id"]
except Exception as e:
state["failed"].append({"id": post["id"],
"title": post["title"],
"error": str(e)})
cursor = posts["pageInfo"]["endCursor"]
state["last_cursor"] = cursor
state["id_map"] = id_map
save_checkpoint(state)
if not posts["pageInfo"]["hasNextPage"]:
break
# Step 4: Rewrite internal links using id_map
rewrite_internal_links(id_map)
print(f"Migration complete. {len(id_map)} cards created. "
f"{len(state['failed'])} failures logged.")This is a structural outline with checkpoint persistence and retry logic, not production-ready code. A real implementation additionally needs: image download/re-upload with URL replacement in HTML, tag creation and assignment, folder creation before card creation, and a comprehensive validation pass comparing source and destination record counts.
Making the Right Call
The Slab-to-Guru migration is primarily a content-format and organizational-model translation problem. The content format gap (Quill Delta → HTML) is solvable with the operation mapping table above and a tested parser. The organizational model gap (many-to-many topics → one-collection-per-card) is the harder design decision that requires agreement from content owners before any code runs.
For small workspaces, the manual Markdown import into Guru gets you most of the way. For anything with real structural complexity, embedded content, or more than a couple hundred posts, an API-based approach is the only path that preserves your information architecture.
If you need help with the pipeline engineering — extraction, Quill Delta parsing, hierarchy mapping, link rewriting, and post-migration validation — reach out to our team for a scoping conversation. We'll tell you honestly whether your workspace is complex enough to warrant external help.
If you're evaluating how Slab compares to other knowledge base targets, check out our Slab to Notion migration guide and Slab to Confluence migration guide.
Frequently Asked Questions
- Can I migrate directly from Slab to Guru?
- There's no one-click migration. Guru has a Slab import option, but it only accepts Markdown files from Slab's manual export. This works for small workspaces but loses topic hierarchy, verification metadata, and internal links. For larger migrations, you need an API-based pipeline.
- Does Slab have an API for data extraction?
- Yes, Slab offers a GraphQL API, but it's only available on Business ($12.50/user/mo) or Enterprise plans. The API supports querying posts, topics, and content using cursor-based pagination. Free and Startup plan users are limited to the manual Markdown/Docx export.
- How do Slab Topics map to Guru Collections?
- Slab allows multiple topics per post (many-to-many), while Guru requires each Card to belong to exactly one Collection. Map the primary Slab topic to a Guru Collection and convert secondary topics to Guru Tags. Sub-topics can become Folders within the Collection, up to three nesting levels.
- What content gets lost during a Slab to Guru migration?
- Potential losses include: embedded third-party content (GitHub, Figma, Loom embeds), version history (Slab doesn't export it), inline images if not re-hosted, internal links if not rewritten, secret topic content if the migration account lacks access, and drafts (not included in manual export).
- What are Guru's API rate limits for migration?
- Guru enforces rate limits per account but does not publicly document specific numeric thresholds. You'll receive HTTP 429 responses when you hit the limit. Build your migration script with exponential backoff and start conservatively at 1-2 requests per second.
