---
title: "How to Migrate from SharePoint to Guru: The Complete Technical Guide"
slug: how-to-migrate-from-sharepoint-to-guru-the-complete-technical-guide
date: 2026-07-10
author: Raaj
categories: [SharePoint, Migration Guide, Guru]
excerpt: "How to migrate from SharePoint to Guru: API constraints, object mapping, permission translation, and step-by-step process for a clean knowledge base migration."
tldr: "Migrating SharePoint to Guru requires converting pages to HTML Cards via API, flattening deep folder hierarchies, and translating item-level permissions to Collection-level access — Guru's native sync is not a migration."
canonical: https://clonepartner.com/blog/how-to-migrate-from-sharepoint-to-guru-the-complete-technical-guide/
---

# How to Migrate from SharePoint to Guru: The Complete Technical Guide


# How to Migrate from SharePoint to Guru: The Complete Technical Guide

> [!NOTE]
> **Quick Answer: The Reality of Migrating SharePoint to Guru**
> Migrating from SharePoint to Guru is a medium-to-high complexity knowledge base migration, not a file copy. It typically takes 2–3 weeks for organizations with 500–2,000 pages, and 3–5 weeks for 5,000+ pages with complex permissions. The single biggest risk is permission misalignment: SharePoint's item-level broken inheritance has no 1:1 equivalent in Guru's Collection-and-Group access model. Version history, SharePoint web parts, and folder nesting beyond three levels cannot be migrated directly. Guru's native SharePoint integration is a **sync**, not a structural migration — it does not create native Guru Cards. Teams under 200 pages with flat permissions can handle this in-house with scripting; above that, or with complex permissions, a managed migration service significantly reduces re-work.

## What Is a SharePoint to Guru Migration?

A **SharePoint to Guru migration** is the process of extracting pages, documents, and metadata from SharePoint Sites and Document Libraries, transforming that content into HTML-formatted Guru Cards, and loading them into Guru's Collection → Folder → Card hierarchy. It is a content-architecture translation, not a file copy.

Guru's own guidance explicitly separates "connect a Source" from "migrate into Guru." Connected sources stay outside Guru and are not editable there. Migrated content becomes editable, governed Guru knowledge. Understanding this distinction is the first step in planning correctly.

### Why Teams Move from SharePoint to Guru

- **AI-powered knowledge delivery.** Guru's search surfaces verified answers inside Slack, Teams, and browser extensions. SharePoint search requires users to navigate to a separate portal, adding context-switching overhead in support and sales workflows where response time matters.
- **Verification workflows.** Guru's built-in Verifier system assigns subject-matter experts to Cards on a configurable recurring interval (e.g., 30, 60, 90, or 180 days). SharePoint has no native equivalent — content review depends on manual processes, SharePoint Designer workflows, or Power Automate flows that must be built and maintained separately.
- **Lower information architecture overhead.** SharePoint requires ongoing admin effort to maintain sites, content types, metadata columns, term stores, and permission inheritance. Guru's flatter hierarchy (Collection → up to 3 Folder levels → Card) reduces structural maintenance for teams under 10,000 articles.
- **In-context access.** Guru operates where support agents and engineers work (Slack, browser extensions, Chrome/Edge), reducing the portal-switching that SharePoint requires for non-admin users.

For a deeper look at SharePoint's information architecture challenges, see our guide on [SharePoint metadata, content types, and lists](https://clonepartner.com/blog/blog/sharepoint-metadata-vs-folders-information-architecture/).

### Three Architectural Differences That Make This Non-Trivial

1. **Permission model.** SharePoint permissions can be broken at the item level, creating thousands of unique access control entries per library. Microsoft supports up to 50,000 unique permissions per list or library but recommends staying under 5,000. Guru controls access at the Collection and Group level — individual Card-level permissions do not exist.
2. **Content format.** SharePoint modern pages use a web-part-based canvas layout (serialized as JSON via the Microsoft Graph API). Guru Cards accept HTML or Markdown content. Every page requires format conversion, including re-hosting of inline images and stripping of unsupported web part types.
3. **Hierarchy depth.** SharePoint allows unlimited site and folder nesting. Guru supports a maximum of three levels of Folder nesting within a Collection. Deep SharePoint structures must be flattened or restructured before loading.

## Data Mapping: SharePoint Objects to Guru Cards

The fundamental data models of SharePoint and Guru do not align cleanly. You must design the correct mapping before writing a single line of extraction code.

| SharePoint Object | Guru Object | Notes / Caveats |
|---|---|---|
| Site | Collection | One Collection per SharePoint Site is the cleanest mapping. Hub Sites may need multiple Collections. |
| Document Library / Folder | Folder | Guru supports max 3 levels of Folder nesting. Deeper SharePoint structures must be flattened. |
| Modern Page / Wiki Page | Card | Page content must be converted from canvas layout JSON or legacy HTML to Guru-compatible HTML. |
| Document (PDF, DOCX, XLSX) | Card with Attachment | Binary files are uploaded via `POST /api/v1/attachments/upload` and referenced in Card HTML. Guru does not extract searchable text from PDFs. |
| List | Card (or external link) | SharePoint Lists have no native Guru equivalent. Convert to tabular Card content or link externally. |
| Metadata Columns | Tags | SharePoint columns (choice, managed metadata) map to Guru Tags. Free-text columns have no clean mapping. |
| Page Author / Modified By | Card Owner / Verifier | Map SharePoint `createdBy` or `lastModifiedBy` to Guru Verifier to preserve accountability. |
| Item-level Permissions | Group-level access on Collection | No 1:1 mapping. Items with broken inheritance must be regrouped into separate Collections or accepted as broader access. |
| Version History | Not migrated | Guru tracks Card revision history natively, but SharePoint's file version history (timestamps, authors, diffs) cannot be imported. |
| Web Parts (Hero, Quick Links, Embed, Power BI) | Not supported | Web part functionality has no Guru equivalent. Content must be manually simplified to static HTML or linked externally. |
| SharePoint Workflows / Power Automate | Not migrated | Guru has no workflow engine. Rebuild in external tools if needed. |

> [!WARNING]
> Do not design a new migration around legacy **Boards** documentation. <cite index="36-1">With the recent updates to hierarchy within Guru, they have switched from Boards to Folders.</cite> Older API docs or scripts referencing `boardId` will fail — use `folderIds` instead.

### What Has No Clean Equivalent in Guru

**SharePoint Lists** are structured tabular data with typed columns, calculated fields, filtering, and views. Guru has no database or list object. Your options:

1. Convert each row to an individual Card (produces high Card counts — a 500-row list becomes 500 Cards).
2. Embed the list as an HTML table inside a single Card (limited to static display, no filtering or sorting).
3. Maintain the List in SharePoint and link from a Guru Card (preserves interactivity but requires cross-platform navigation).

**Item-level permissions with broken inheritance** are the hardest translation problem. A Document Library with 200 unique permission entries cannot be expressed in Guru's model. The practical solution: audit which permission groups actually exist and which users they contain, then restructure content into separate Guru Collections with appropriate Group assignments. In practice, most libraries have 3–8 distinct access patterns, not 200 — many "unique" permission entries are near-duplicates.

**Field-level mapping** is where most DIY projects slow down. Rich text usually maps cleanly after HTML cleanup (stripping SharePoint-specific CSS classes and `data-` attributes). Choice and managed metadata columns become Guru Tags via `POST /api/v1/tagcategories` and `POST /api/v1/tagcategories/{id}/tags`. People fields are best mapped to **Verifier** assignments or rendered as a visible metadata line in the Card body. If your SharePoint information architecture is folder-heavy and metadata-light, restructure before migration — otherwise you recreate opaque storage in a different UI.

## Migration Approaches: Native Sync vs. Custom ETL vs. Managed Service

There are three primary viable approaches. Generic SharePoint file-migration tools (SPMT, Migration Manager, ShareGate) move content into Microsoft 365 destinations, not into Guru Cards — they do not solve the Card-conversion problem.

### Guru's Native SharePoint Integration Is a Sync, Not a Migration

<cite index="11-15,11-16">Guru lets you connect SharePoint as a Source to improve knowledge visibility, discoverability, and governance across your organization. By syncing SharePoint content into Guru, teams can easily search, access, and trust internal documentation.</cite> But this is an **ongoing sync** — content remains in SharePoint and is surfaced through Guru's search. It does not create native Guru Cards that your team can edit, verify, or restructure within Guru.

The upside is speed (minutes to configure) and zero coding. The downside: synced content is read-only in Guru, and it carries a hard limitation. <cite index="11-23">Due to a limitation in the Microsoft Graph API, SharePoint Group memberships cannot be retrieved — so while Guru can see the groups, they appear empty, and Access Control Lists (ACLs) won't sync automatically for SharePoint group-only sites.</cite> Guru also enforces a **50 MB file-size limit** across connected sources. Files exceeding this limit are silently excluded from the sync.

**When to use native sync:** You want to keep SharePoint as the system of record but make content discoverable in Guru's search and browser extension. You are not moving off SharePoint.

### Custom Microsoft Graph → Guru API ETL

This is the right path for a true **structural migration** into native, editable Guru Cards. You extract with the Microsoft Graph API, transform modern pages and file content into clean HTML, create Collections and Folders via the Guru API, then create Cards and assign Verifiers.

The upside is full control: you can rewrite internal links, flatten bad hierarchies, normalize metadata, deduplicate content, and preserve accountability. The downside is significant engineering work — retry logic, pagination handling, attachment re-hosting, permission modeling, link rewriting, and QA all become your team's responsibility.

**Typical engineering effort:** 40–80 hours for a developer experienced with both APIs, for a 500–2,000 page environment. This does not include content audit, stakeholder alignment, or UAT time.

### Guru Manual Sync / SDK-Based Import

Guru supports both **imports** (one-time, content becomes editable in Guru) and **syncs** (content is read-only, refreshable from the source bundle). The Manual Sync path loads a structured ZIP file into an `EXTERNAL` collection. The ZIP must follow Guru's expected bundle format: a manifest JSON file mapping content files to Cards, with HTML content files referenced by path.

This works well for archive-like or externally mastered content. It is weaker when you need rich per-Card governance inside Guru after launch, because synced content stays read-only. Complexity is medium if your source is already normalized and you can generate the expected bundle format programmatically.

**Note on Guru SDKs:** As of 2024, Guru does not publish an official Python or Node SDK. Migrations use the REST API directly via `requests` (Python), `axios`/`fetch` (Node), or community wrappers. The API is straightforward enough that a dedicated SDK is not strictly necessary.

### Managed Migration Service

A specialized team handles the end-to-end extraction, transformation, loading, and validation. Best for 500+ pages, complex permissions, compliance requirements (SOC 2, HIPAA content handling), or when your engineering team cannot absorb a multi-week project without delaying product work.

| Approach | How It Works | Best For | Complexity | Key Risks |
|---|---|---|---|---|
| **Guru Native SharePoint Sync** | Connects SharePoint as a read-only Source via delegated Graph permissions | Teams keeping SharePoint as source-of-truth while enabling Guru search | Low | Not a migration; no native Cards; SharePoint Group ACLs don't sync; 50 MB file limit |
| **Custom API ETL (Python/Node)** | Extract via Graph API, transform HTML, load via Guru REST API | Engineering teams with 200–2,000 pages and 40–80 dev hours available | High | Throttling on both sides; attachment re-hosting; broken internal links; undocumented Guru rate limits |
| **Guru Manual Sync / SDK** | Upload structured ZIP bundle into Guru | Controlled one-way sync or archive import | Medium | Read-only if synced; must match Guru's expected bundle format exactly |
| **Managed Migration Service** | Dedicated team handles end-to-end ETL, permission mapping, and validation | 500+ pages, complex permissions, compliance requirements | Low (for your team) | Cost; requires vendor evaluation and data-handling agreements |

**Decision criteria by scenario:**
- **Under 50 pages, no complex formatting:** Manual copy-paste or Guru's native sync.
- **50–200 pages, flat permissions:** Guru CSV import (for simple text content) or lightweight scripting.
- **200–2,000 pages, dev team available:** Custom API ETL with careful throttle management. Budget 40–80 engineering hours.
- **2,000+ pages or complex permissions (broken inheritance in >10% of libraries):** Managed migration service.
- **Ongoing sync, not a one-time move:** Guru's native SharePoint integration. Middleware tools (Zapier, Make, n8n) can handle incremental Card creation via Guru's API triggers, but they are not designed for bulk backfill (Zapier's task limits and execution timeouts make them impractical for more than ~100 records per run).

## Extracting SharePoint Data: API Limits and the 5,000-Item Threshold

SharePoint data extraction uses the **Microsoft Graph API** (preferred) or the older SharePoint REST API. The Graph API consumes fewer resource units for equivalent operations and provides consistent endpoint patterns across SharePoint, OneDrive, and Teams.

### Key API Constraints

- **User-level throttling:** <cite index="22-8,28-5">SharePoint Online enforces a default limit of 3,000 requests per 5 minutes per user account</cite>, with additional per-second limits depending on the operation type. In practice, sustained throughput of ~8–10 requests/second per user is safe for read operations.
- **App and tenant throttling:** <cite index="21-17,21-18">Limits are also applied to applications in a tenant, based on the number of licenses purchased per organization</cite>. Costs are measured in resource units, and different API calls consume different amounts. A `GET` on a list item costs 1 resource unit; a search query costs 10.
- **The 5,000-item list view threshold:** <cite index="41-2">By default, the list view threshold is configured at 5,000 items</cite>. <cite index="43-1,43-2">This is a query performance limit where views attempting to retrieve more than 5,000 items without proper indexing may experience throttling or timeouts — the threshold applies per view query, not to total library size</cite>. SharePoint can store 30 million items in a list or library, but unfiltered export queries will fail at 5,000. Partition queries using indexed column filters (`$filter` on an indexed field), folder-scoped requests, or time-window segmentation.
- **Throttle responses:** <cite index="21-7">SharePoint Online returns HTTP status code 429 ("Too many requests") or 503 ("Server Too Busy"), and the requests will fail</cite>. Always honor the `Retry-After` header. The header value is in seconds.
- **Bandwidth caps:** <cite index="28-8">50 GB ingress per hour and 100 GB egress per hour</cite> per user account. For a typical migration (pages + documents under 10 GB total), this is not a binding constraint.
- **Search throttling:** Microsoft documents 10 delegated search requests per second per user and 25 app-only search requests per second per tenant for the Search API. Use search sparingly during extraction — prefer direct list/library enumeration.

### Extracting Pages via Graph API

Use the SharePoint Pages API to retrieve page content including the canvas layout:

```http
GET /sites/{site-id}/pages/microsoft.graph.sitePage?$expand=canvasLayout
```

This returns the page's web part structure as JSON, including `textWebPart` inner HTML that can be extracted and cleaned for Guru. Image web parts return URLs that must be downloaded and re-hosted. Pagination follows standard OData `@odata.nextLink` patterns — continue fetching until `@odata.nextLink` is absent from the response.

For document libraries, use delta queries (`/drives/{drive-id}/root/delta`) — <cite index="21-31,21-32">delta with a token is the most efficient way to scan content in SharePoint, and Microsoft lowers the resource unit cost of delta requests to 1 resource unit</cite>. Store the delta token between runs for incremental re-scans during pilot iterations or pre-cutover syncs.

A production-grade extractor typically hits these endpoints:

- **Site discovery:** `GET /sites` or `GET /sites?search={query}` — returns all sites the authenticated user/app can access.
- **Lists and libraries:** `GET /sites/{site-id}/lists` — filter by `list.template` to distinguish Document Libraries (`documentLibrary`) from generic lists.
- **Library folder/file traversal:** `GET /sites/{site-id}/drive/items/{item-id}/children` with `$select`, `$expand`, and `$skipToken` for pagination.
- **File content download:** `GET /sites/{site-id}/drive/items/{item-id}/content` — returns the raw binary stream.
- **Modern pages:** `GET /sites/{site-id}/pages/microsoft.graph.sitePage` — requires `Sites.Read.All` permission scope.
- **Delta export:** `GET /drives/{drive-id}/items/{item-id}/delta` for incremental re-scans during pilot or cutover phases.

### Permissions Deserve a Separate Export Pass

SharePoint supports up to **50,000 unique permissions** in a list or library, but Microsoft recommends **5,000** as a practical limit for performance. When a list, library, or folder exceeds 100,000 items, you cannot break or reinherit permissions at that container level.

To extract permissions, use `GET /sites/{site-id}/drive/items/{item-id}/permissions` for each item with broken inheritance. This is expensive — each item requires a separate API call. For large libraries, first identify which items have broken inheritance by checking the `hasUniqueRoleAssignments` property via the SharePoint REST API (`/_api/web/lists('{list-id}')/items?$filter=...`), then selectively extract only those permissions.

If your security model depends on item-level exceptions, normalize the permission structure before mapping it into Guru. In practice, this means creating a permission audit spreadsheet that maps each unique access pattern to a proposed Guru Collection + Group assignment.

For a full breakdown of SharePoint export methods, see our guide: [How to Export Data from SharePoint: Methods, Limits & Migration Prep](https://clonepartner.com/blog/blog/how-to-export-data-from-sharepoint-methods-limits-migration-prep/).

## Loading Content into Guru: API Constraints and Formatting

Guru's REST API is at `https://api.getguru.com/api/v1/`. Authentication uses HTTP Basic Auth with your email and API token (Base64-encoded `email:token` in the `Authorization` header). For migrations, you need a **User Token** (read/write access), not a Collection Token (read-only, scoped to a single Collection).

### Card Creation Endpoint

Create a Card with a single `POST` to `/api/v1/cards/extended`:

```json
{
  "preferredPhrase": "Card Title",
  "content": "<p>HTML content here</p>",
  "shareStatus": "TEAM",
  "collection": { "id": "COLLECTION_UUID" },
  "folderIds": ["FOLDER_UUID_1"],
  "tags": [{ "value": "tag-name" }],
  "verificationInterval": 90,
  "verifiers": [
    {
      "type": "user",
      "user": { "email": "verifier@company.com" }
    }
  ]
}
```

<cite index="31-3,31-4,31-5,31-6">Content and title are required. Card share status should be set to TEAM to make a card team-shared. Omitting the share status or setting it to PRIVATE will result in a card only accessible by the card owner. This endpoint allows the verifier to be set upon card creation, rather than requiring a separate call.</cite>

Key payload notes:
- `content` accepts HTML. Guru strips JavaScript, iframes, and `<script>` tags. Allowed elements include `<p>`, `<h1>`–`<h6>`, `<ul>`, `<ol>`, `<li>`, `<table>`, `<a>`, `<img>`, `<strong>`, `<em>`, `<code>`, `<pre>`, and `<blockquote>`.
- `folderIds` accepts an array, but in practice a Card should belong to one Folder for clarity.
- `tags` must reference existing Tag values. Create Tag Categories and Tags first via `POST /api/v1/tagcategories` and `POST /api/v1/tagcategories/{id}/tags`.
- `verificationInterval` is in days. Common values: 30, 60, 90, 180, 365.

### Attachment Upload

<cite index="38-15">To upload a file 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.</cite> The response returns a JSON object containing an attachment URL (hosted on Guru's CDN) that you reference in the Card's HTML body via an `<a>` or `<img>` tag. Each attachment is a separate API call — there is no batch endpoint.

Maximum attachment file size is governed by your Guru plan. Files uploaded via the API must complete within the HTTP timeout window — for files over 20 MB, monitor for timeout errors and retry.

### Guru's Rate Limits Are Not Publicly Documented

<cite index="4-1">Rate limits are enforced but not publicly documented.</cite> <cite index="4-2,4-3">Pagination uses a cursor-based `nextPage` token; standard offset/limit parameters are not supported. The API does not support bulk operations — each invite or role update is a discrete request.</cite> The same applies to Card creation: every Card is an individual `POST`.

<cite index="6-1,6-2">Guru enforces a rate limit per account. If you receive this response, back off for a few seconds before retrying.</cite>

Based on empirical testing across multiple migrations, a safe sustained throughput is **2–3 Card creation requests per second**. Bursting above 5 requests/second reliably triggers 429 responses within 30–60 seconds. Your migration script **must** implement exponential backoff with jitter. Without it, a script creating 2,000+ Cards will stall unpredictably.

> [!WARNING]
> Guru's API has no bulk Card creation endpoint. Every Card, attachment, and tag assignment is an individual HTTP request. At 2–3 Cards/second sustained throughput:
>
> - **500 Cards** ≈ 3–4 minutes of API time
> - **2,000 Cards** ≈ 11–17 minutes of API time
> - **10,000 Cards** ≈ 55–85 minutes of API time
> - **50,000 Cards** ≈ 5–7 hours of API time
>
> These estimates exclude transformation, attachment upload, and link-rewriting passes. Attachment uploads (especially for PDFs and images) typically double the total elapsed time.

## Step-by-Step Migration Process [For Engineering]

A production-grade migration requires a strict order of operations to prevent orphaned records and broken links.

### Step 1: Audit and Inventory SharePoint Content

Enumerate all Sites, Document Libraries, and Pages using the Graph API. Record:

- Item counts per library (flag any library exceeding 5,000 items for paginated extraction with indexed column filters)
- Content types per item (modern page, wiki page, DOCX, PDF, XLSX, other)
- Folder depth per library (flag anything deeper than 3 levels)
- Permission structure: identify every library or folder with broken inheritance using `hasUniqueRoleAssignments`
- Last modified dates (identify stale content for exclusion)
- Total storage size (estimate attachment upload time)

Export a ledger keyed by SharePoint URL or item ID. This ledger becomes your source-to-target mapping table for the entire migration.

**Recommended exclusions:** SharePoint system pages (`_layouts`, `_catalogs`), auto-generated site pages (home.aspx with only web parts and no authored content), files not modified in >24 months (unless compliance requires retention).

### Step 2: Design the Guru Information Architecture

Map SharePoint Sites to Guru Collections. Flatten folder structures deeper than three levels — common patterns:

- **SharePoint:** `Site > Library > Folder A > Folder B > Folder C > Folder D` → **Guru:** `Collection > Folder A > Folder B` with Folder C and D content merged or tagged
- **SharePoint Hub Site with 5 sub-sites:** → **Guru:** 5 Collections, or 1 Collection with top-level Folders per sub-site (depending on permission requirements)

Define your Tag taxonomy based on SharePoint metadata columns. Map `Choice` columns to Tag Categories; map `Managed Metadata` term sets to hierarchical Tags. Document which SharePoint authors become Guru Verifiers — use page owners or content stewards, not the migration service account.

Get stakeholder sign-off on the Collection structure before extraction begins. Restructuring Collections after migration requires moving Cards one at a time via the API.

### Step 3: Extract Content from SharePoint

Use the Graph API Pages endpoint for modern pages (`/sites/{id}/pages/microsoft.graph.sitePage?$expand=canvasLayout`). For documents, use the DriveItem API (`/drives/{id}/items/{id}/content`). Pull modern pages separately from library files — they transform differently.

Implement retry logic that honors `Retry-After` headers. Run extraction during off-peak hours (evenings, weekends) to reduce throttling probability.

```python
import requests
import time

def extract_with_retry(url, headers, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers)
        if resp.status_code == 200:
            return resp.json()
        elif resp.status_code in (429, 503):
            wait = int(resp.headers.get("Retry-After", 2 ** attempt))
            print(f"Throttled. Waiting {wait}s (attempt {attempt + 1}/{max_retries})")
            time.sleep(wait)
        else:
            resp.raise_for_status()
    raise Exception(f"Failed after {max_retries} retries: {url}")
```

Store all extracted content locally (JSON for pages, binary for files) with the SharePoint item ID as the filename. This creates a local cache that decouples extraction from transformation — if the transform step fails, you don't need to re-extract.

### Step 4: Transform Content to Guru-Compatible HTML

SharePoint modern pages return a `canvasLayout` JSON structure containing sections, columns, and web parts. The transformation pipeline:

1. **Extract text content:** Pull `innerHTML` from `textWebPart` sections. This is already HTML but contains SharePoint-specific CSS classes and `data-sp-*` attributes that should be stripped.
2. **Handle images:** `imageWebPart` sections contain `serverRelativeUrl` references. Download each image, upload to Guru's attachment endpoint, and replace the `src` URL in the HTML.
3. **Strip unsupported web parts:** Remove `heroWebPart`, `quickLinksWebPart`, `embedWebPart`, `powerBIWebPart`, and any custom web parts. Log each removal for manual review.
4. **Flatten multi-column layouts:** SharePoint sections can have 1, 2, or 3 columns. Guru Cards do not support multi-column layouts. Merge columns top-to-bottom in left-to-right order.
5. **Clean HTML:** Remove empty `<div>` wrappers, normalize heading levels (SharePoint pages often start at `<h2>`), and ensure all `<a>` tags have valid `href` attributes.
6. **Handle binary files:** Decide per file type:
   - **PDF:** Upload as attachment, create a Card with a download link and a brief description. Guru does not extract searchable text from PDF attachments.
   - **DOCX:** Convert to HTML using a library like `python-docx` + `mammoth`, then load as Card content. Alternatively, upload as attachment.
   - **XLSX:** Convert key data to an HTML table, or upload as attachment with a summary Card.

```python
from bs4 import BeautifulSoup

def transform_sharepoint_html(raw_html: str) -> str:
    soup = BeautifulSoup(raw_html, "html.parser")
    # Remove SharePoint-specific attributes
    for tag in soup.find_all(True):
        attrs_to_remove = [a for a in tag.attrs if a.startswith("data-sp")]
        for attr in attrs_to_remove:
            del tag[attr]
    # Remove empty divs
    for div in soup.find_all("div"):
        if not div.get_text(strip=True) and not div.find("img"):
            div.decompose()
    return str(soup)
```

### Step 5: Create the Guru Hierarchy First

**Order of operations matters.** Create objects in this sequence to prevent broken references:

1. **Tag Categories and Tags** — create these first so Card creation can reference them. Store `tag_category_id` and `tag_id` mappings.
2. **Collections** — `POST /api/v1/collections`. Store the returned Collection UUIDs.
3. **Folders** within Collections — `POST /api/v1/folders`. Persist the mapping `sharepoint_path → guru_folder_id`. Create parent folders before child folders.
4. **Attachments** — upload individually via `POST /api/v1/attachments/upload`. Store `sharepoint_file_url → guru_attachment_url` mapping.
5. **Cards** — set collection ID, folder IDs, tags, verifier, and verification interval in a single call to `POST /api/v1/cards/extended`. Persist `sharepoint_url → guru_card_id` for the link-rewriting pass.

Assign Verifiers from your business owner map, not from the migration service account. If the migration script's user account becomes the default Verifier, every Card will show the migration engineer as the subject-matter expert.

### Step 6: Load Cards with Exponential Backoff

Push the transformed HTML into Guru as Cards. Implement exponential backoff with jitter to handle undocumented rate limits:

```python
import random, time
import requests

def create_guru_card(payload, auth, max_retries=5):
    delay = 2
    for attempt in range(max_retries):
        response = requests.post(
            "https://api.getguru.com/api/v1/cards/extended",
            json=payload,
            auth=auth
        )
        if response.status_code in (200, 201):
            return response.json()
        elif response.status_code == 429:
            wait = int(response.headers.get("Retry-After", delay))
            jitter = random.uniform(0, 0.5)
            print(f"Rate limited. Waiting {wait + jitter:.1f}s (attempt {attempt + 1})")
            time.sleep(wait + jitter)
            delay = min(delay * 2, 60)
        else:
            raise Exception(f"Failed to create card (HTTP {response.status_code}): {response.text}")
    raise Exception(f"Failed after {max_retries} retries")
```

Log every Card creation with its SharePoint source URL and Guru Card ID. This log is your audit trail and your input for Step 7.

### Step 7: Rebuild Internal Links

This is the step most DIY scripts skip — and the step that causes the most post-launch complaints. Once all Cards exist, run a second pass over every Card body:

1. Retrieve each Card's HTML via `GET /api/v1/cards/{card-id}`
2. Scan for SharePoint URLs (`https://tenant.sharepoint.com/sites/...`)
3. Replace each SharePoint URL with the corresponding Guru Card URL using the `sharepoint_url → guru_card_id` mapping from Step 5
4. Update the Card via `PUT /api/v1/cards/{card-id}`

```python
import re

def rewrite_links(html: str, url_map: dict) -> str:
    """Replace SharePoint URLs with Guru Card URLs."""
    for sp_url, guru_card_id in url_map.items():
        guru_url = f"https://app.getguru.com/card/{guru_card_id}"
        html = html.replace(sp_url, guru_url)
    return html
```

Track URLs that appear in Card bodies but have no mapping entry — these are links to SharePoint content that was excluded from migration (e.g., lists, workflows, external sites). Log them for manual resolution.

### Step 8: Delta Sync Before Cutover

Use delta queries (`GET /drives/{drive-id}/items/{item-id}/delta`) and a last-modified window for pages changed during the pilot phase. The delta sync workflow:

1. Store the delta token from your initial extraction
2. Re-query with the stored token to get only changed items
3. Re-transform and re-upload only changed Cards
4. Lock SharePoint to read-only (set site to "No Access" for non-admin users, or use site lock via SharePoint admin PowerShell: `Set-SPOSite -Identity <URL> -LockState ReadOnly`) before the final extraction
5. Run one final delta pass to capture any last-minute changes
6. Perform the link-rewriting pass again for any new Cards

The freeze period between SharePoint read-only lockdown and Guru go-live should be **24–48 hours** to allow for the final extraction, transformation, loading, link rewriting, and validation.

## Timeline, Phases, and Resourcing [For PMs]

Do not attempt a "big bang" cutover for enterprise environments. Use a phased approach.

| Phase | Duration (500–2,000 pages) | Dependencies |
|---|---|---|
| Content audit & inventory | 2–3 days | SharePoint admin access, Graph API app registration with `Sites.Read.All` scope |
| IA design & mapping | 2–3 days | Stakeholder sign-off on Collection structure |
| Script development & testing | 3–5 days | Developer with Graph API and REST API experience; Guru API token |
| Pilot migration (10% of content) | 1–2 days | Test Collection in Guru; 3–5 content owners for review |
| Full migration run | 1–2 days | Off-peak hours for SharePoint extraction |
| Link rewriting & attachment validation | 1 day | Completed source-to-target URL mapping |
| Validation & UAT | 2–3 days | Content owners review assigned Cards; permission spot-checks |
| Go-live & redirect setup | 1 day | DNS/link redirect plan; SharePoint read-only lock |
| **Total** | **13–20 business days** | |

For organizations under 200 pages, compress this to 5–7 days. For 5,000+ pages with complex permissions, expect 4–6 weeks including the permission audit and restructuring phase.

The timeline is driven less by raw volume than by **permission cleanup, hierarchy flattening, and link rewriting**. A 5,000-page environment with flat permissions and no broken inheritance moves faster than a 1,000-page environment with hundreds of unique permission entries.

### Estimated Engineering Hours by Migration Size

| Content Volume | Estimated Engineering Hours | Primary Time Sink |
|---|---|---|
| Under 200 pages | 16–24 hours | Script development and testing |
| 200–500 pages | 30–50 hours | Transform logic for varied content types |
| 500–2,000 pages | 50–80 hours | Permission mapping and link rewriting |
| 2,000–5,000 pages | 80–160 hours | QA, edge cases, attachment volumes |
| 5,000+ pages | 160+ hours | Permission restructuring, multi-phase rollout |

### Risk Register

| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| SharePoint API throttling stalls extraction | High | Medium | Implement `Retry-After` handling; run during off-peak hours; use delta queries (1 resource unit each) |
| Guru rate limits block Card creation | Medium | Medium | Exponential backoff with jitter; throttle to 2–3 requests/second; parallelize across multiple Guru user accounts if needed |
| Broken internal links between Cards | High | High | Two-pass migration: create Cards first, then update links using URL → Card ID map; log unmapped URLs |
| Permission escalation (restricted content becomes broadly visible) | Medium | Critical | Audit permissions pre-migration; map to separate Collections; test with least-privileged user accounts before go-live |
| Image/attachment rendering failures | Medium | Medium | Validate attachment URLs post-upload; re-host all images via Guru attachment endpoint; check for expired SharePoint auth tokens in cached URLs |
| Deep hierarchy becomes unusable in Guru | Medium | Medium | Flatten before import; split into multiple Collections; use Tags to preserve former hierarchy metadata |
| Content drift during migration window | Medium | Medium | Lock SharePoint to read-only 24–48 hours before cutover; run delta sync before final load |
| Duplicate Cards from duplicate SharePoint pages | Low | Medium | Deduplicate during transform phase using content hashing or URL normalization |

## Preserving Knowledge Trust: Verifiers and History [For Customer Success]

When your team moves from SharePoint to Guru, end-users lose the implicit trust signals they relied on: "I know this page is current because Sarah updated it last month." In Guru, that trust is rebuilt through the **Verifier system**.

<cite index="32-6">Cards are Guru's core knowledge units — short, searchable, and organized within folders and Collections, with built-in collaboration and version tracking.</cite> Each Card can be assigned a Verifier (an individual user or a Group) and a Verification Interval. <cite index="51-6,51-7">Guru comes with a feature to verify your knowledge so that users can trust that the content is the most up-to-date. Guru regularly prompts experts to verify the information so your knowledge base always remains accurate.</cite>

**Map SharePoint page owners to Guru Verifiers during migration, not after.** The `/api/v1/cards/extended` endpoint accepts a `verifiers` array and a `verificationInterval` (in days) at creation time. If you skip this, every Card launches as "Unverified," and your team inherits a backlog of hundreds of verification tasks on day one — which typically means nobody verifies anything.

Recommended verification intervals by content type:
- **Policy documents, compliance content:** 90 days
- **Process documentation, how-to guides:** 180 days
- **Product knowledge, feature docs:** 90–120 days (aligned with release cycles)
- **Onboarding materials:** 180–365 days
- **Troubleshooting guides:** 90 days

SharePoint version history does not transfer to Guru. Guru tracks its own Card revision history (each edit creates a new version with timestamp and author), but importing SharePoint's historical revision trail with original timestamps and authors is not supported. Communicate this to stakeholders before migration. If historical audit trails matter for compliance (SOX, HIPAA, internal audit), archive the SharePoint site in read-only mode before decommissioning. Consider exporting version history to a separate archive (CSV or document) for records retention.

### Post-Migration Governance Playbook

After go-live, establish these governance practices to prevent the knowledge decay that likely motivated the migration from SharePoint:

1. **Verification dashboard review:** Guru admins should review the "Unverified Cards" dashboard weekly for the first 30 days, then monthly. Set a team OKR for verification compliance (target: >90% of Cards verified within their interval).
2. **New content creation process:** Define where new knowledge is created (Guru, not SharePoint). Disable write access to the old SharePoint site after 30 days.
3. **Ongoing content ingestion:** For content that continues to originate in SharePoint (e.g., from teams not yet migrated), use Guru's native sync for read-only visibility or build a lightweight scheduled script that checks for new/modified SharePoint pages and creates corresponding Guru Cards.
4. **Tag governance:** Assign a Tag taxonomy owner. Review and consolidate Tags quarterly to prevent sprawl.
5. **Analytics review:** Use Guru's analytics to identify Cards with zero views after 60 days — these are candidates for archival or consolidation.

### What Users Will Notice After Go-Live

Users will notice **faster retrieval and clearer ownership**, but they will also notice that Guru is Card-first, not page-layout-first. Specific changes to communicate:

- Some SharePoint pages become multiple Cards (one Card per discrete topic is a Guru best practice; Guru recommends Cards be "short and scannable")
- Some files remain as attachment links rather than inline content
- Permissions may be broader at the Collection level than they were item-by-item in SharePoint — communicate any access changes to affected teams before go-live
- Multi-column layouts, embedded dashboards, and interactive elements are not available in Guru's editor
- Search behavior is different: Guru's AI-powered search returns Card-level results with AI-generated answers, not page-level results with keyword highlights

A practical pattern: map each SharePoint site owner, library owner, or knowledge steward to a **Guru Verifier Group**. That gives customer-facing and support teams a visible answer to "who owns this article?" and keeps AI-grounded answers anchored to reviewed content rather than orphaned documents.

Communicate the freeze period clearly. SharePoint should become read-only at least 24–48 hours before the final cutover to prevent content drift during the transition.

## Edge Cases and Known Limitations

- **The 5,000-item list view threshold.** If you filter or sort a SharePoint document library containing over 5,000 items via the Graph API without indexed columns, the query will fail or return throttled responses. Partition by folder-scoped requests, indexed column `$filter` expressions, or `$skipToken` pagination.
- **Item-level broken inheritance.** Guru does not support Card-level permissions. A Card inherits its Collection's access settings. If different SharePoint pages within the same library have different access requirements, they must be split across separate Guru Collections — each with its own Group assignments.
- **Unsupported web parts.** SharePoint pages containing dynamic web parts (Power BI dashboards, Hero sections, Quick Links carousels, Embed/iframe, Yammer feeds, Stream video) will not migrate as functional elements. Content must be simplified to static HTML, screenshots with alt text, or external links.
- **Guru's editor is simpler than SharePoint's.** <cite index="59-10,59-11">Guru's Card editor allows you to format content for clarity and readability. The editor supports Markdown, and allows embedding of images, files, videos, and more.</cite> However, complex layouts, multi-column sections, collapsible sections, tabs, and interactive elements are not supported. Guru Cards are designed to be short, focused knowledge units — not long-form documents.
- **SharePoint Group ACLs in native sync.** Guru's native SharePoint integration documents that the Graph API cannot retrieve SharePoint Group memberships, so ACLs won't sync for sites that rely exclusively on SharePoint groups (as opposed to Microsoft 365 groups or Azure AD security groups).
- **Large files in Source sync.** Guru applies a **50 MB file-size limit** across connected sources. Files exceeding this limit are silently excluded.
- **PDF text extraction.** Guru does not extract searchable text from PDF attachments. PDFs are stored as downloadable files. If PDF content needs to be searchable, extract the text during transformation and include it in the Card body.
- **No built-in deduplication.** If your SharePoint environment has duplicate pages across multiple sites (common with hub sites and content rollups), you will create duplicate Cards unless you deduplicate during the transform phase. Use content hashing (MD5/SHA256 of normalized HTML) or URL-based dedup rules.
- **Large attachment volumes.** Guru's attachment upload is per-file with no batch endpoint. A library with 2,000 PDFs averaging 2 MB each takes approximately 45–90 minutes to upload at sustained throughput, depending on network and rate limits.
- **Deep folder trees.** Guru supports up to **3 nested folder levels**. Anything deeper must be flattened, merged, or split into multiple Collections before migration. Use Tags to preserve the former hierarchy path for searchability.
- **Card content size limits.** Guru does not publicly document a maximum Card content size, but empirically, Cards exceeding ~100 KB of HTML content may experience editor performance issues. Long SharePoint pages should be split into multiple Cards.
- **Managed metadata term store.** SharePoint's managed metadata (term store) supports hierarchical, multilingual term sets. Guru Tags are flat (no hierarchy). Multi-level term sets must be flattened to leaf-node Tags or concatenated (e.g., "Region > EMEA > UK" becomes the Tag "Region-EMEA-UK").

## Rollback Procedure

If the migration produces unacceptable results (permission escalation, widespread formatting failures, missing content), execute this rollback:

1. **Revert SharePoint to read-write:** Remove the site lock (`Set-SPOSite -Identity <URL> -LockState Unlock`).
2. **Communicate to users:** Send a brief notification that Guru is temporarily unavailable and SharePoint is the active knowledge base.
3. **Archive or delete Guru Collections:** Use `DELETE /api/v1/collections/{id}` to remove migrated Collections. This deletes all Cards and Folders within them. Alternatively, restrict Collection access to admins only while you investigate.
4. **Diagnose root cause:** Use your source-to-target ledger to identify which Cards have issues. Common root causes: HTML transformation errors, missing attachment re-hosting, permission mapping errors.
5. **Fix and re-run:** Correct the transformation or mapping logic and re-run from Step 4 onward (the extraction cache from Step 3 is still valid).
6. **Re-validate:** Run the full validation checklist before attempting go-live again.

Keep the SharePoint site active in read-only mode for at least **30 days** post-successful-migration. Retain your source-to-target ledger indefinitely — it's your only mapping between old URLs and new Card IDs for redirect rules and troubleshooting.

## Validation and Testing

Validation determines whether the migration succeeded or shipped a broken knowledge base. Run these checks before granting go-live approval:

1. **Record-count reconciliation.** Total Cards in Guru must equal total migrated pages from SharePoint, minus explicitly excluded items (archived pages, system pages, duplicates removed). Automate this check: `GET /api/v1/collections/{id}/cards` and compare counts by Collection against your extraction ledger.
2. **Field-level spot check.** Sample 10–15% of Cards (minimum 20 Cards for small migrations). Verify: title accuracy, HTML rendering (no raw HTML tags visible), image display (no broken image icons), correct Collection/Folder placement, Tag assignment, and Verifier assignment.
3. **Link integrity scan.** Crawl all Card HTML for URLs. Flag: (a) remaining SharePoint URLs that should have been rewritten, (b) broken `<img>` src URLs pointing to expired SharePoint auth tokens, (c) links to migrated pages that were excluded and have no Guru equivalent.
4. **Permission audit.** Confirm that restricted SharePoint content landed in correctly permissioned Guru Collections. Test with a non-admin, least-privileged user account from each major access group. If restricted HR, legal, or executive documents are visible to unauthorized users, halt go-live and fix Collection permissions immediately.
5. **Search validation.** Test 10–15 search queries that users actually use (gathered from SharePoint search logs or support ticket analysis). Confirm that relevant Cards surface in the top 3 results. Test both exact-title searches and natural-language queries.
6. **UAT with content owners.** Have 3–5 subject-matter experts review their sections end-to-end before go-live. Provide a structured review template: "Is the content accurate? Are images rendering? Is the Verifier correct? Are any pages missing?"
7. **Attachment verification.** Download 5–10 attachments from Guru Cards and confirm they match the original SharePoint files (file size comparison or hash check).
8. **Rollback readiness.** Confirm SharePoint is still accessible in read-only mode. Verify your source-to-target ledger is complete and backed up. Test the `DELETE /api/v1/collections/{id}` endpoint on a test Collection to confirm rollback mechanics work.

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

## Build In-House vs. Use a Managed Service

**Build in-house when:** your content volume is under 500 pages, your permissions model is straightforward (no broken inheritance or fewer than 5 unique access patterns), you have a developer experienced with REST APIs who can commit 40–80 hours over 2–3 weeks, and you don't need to preserve complex formatting (multi-column layouts, embedded dashboards).

**Use a managed service when:** you have 1,000+ pages, complex permission structures with broken inheritance across multiple libraries, attachment volumes exceeding 1,000 files, compliance requirements around data handling (SOC 2, HIPAA, data residency), or your engineering team cannot absorb a multi-week migration project without delaying product work.

The hidden cost of DIY is not the initial script — it's the re-work. Broken links, missing attachments, permission misalignment, and Guru API rate limit failures typically surface 1–2 weeks after go-live, when content owners start actually using the system. Based on our experience, fixing these issues retroactively takes 30–50% as many engineering hours as the original migration.

ClonePartner has completed knowledge base migrations involving complex permission models, large attachment volumes, and formatting transformations across platforms. For migrations where the SharePoint source involves broken inheritance and the target requires clean Guru Collection-level access, our team handles the permission audit and restructuring as part of the project — your engineers stay on product work. For a deeper look at our approach, see [how we run migrations](https://clonepartner.com/blog/blog/how-we-run-migrations-at-clonepartner/) and our [zero-downtime guarantee](https://clonepartner.com/blog/blog/zero-downtime-data-migration/).

> Need to move your SharePoint knowledge base into Guru without broken links, missing attachments, or permission gaps? Book a 30-minute call and get a migration plan specific to your environment.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can I migrate SharePoint to Guru without losing data?

Page content, titles, tags, and author information can be fully migrated to Guru Cards. However, SharePoint version history, web part interactivity, item-level permissions, and Power Automate workflows cannot be replicated in Guru. Binary file attachments can be re-hosted but remain as Card attachments, not a document management system.

### Does Guru have a native SharePoint import tool?

No. Guru offers a native SharePoint sync integration that surfaces SharePoint content in Guru's search, but it does not create editable native Guru Cards. For a structural migration where content lives natively in Guru, you need the Guru REST API, manual recreation, or a managed migration service.

### How long does a SharePoint to Guru migration take?

For 500–2,000 pages with a dedicated engineer: 2–3 weeks including audit, script development, migration, and validation. Under 200 pages with simple formatting: 5–7 days. For 5,000+ pages with complex permissions: 3–5 weeks.

### What data cannot be migrated from SharePoint to Guru?

SharePoint version history, Power Automate workflows, web part functionality (Hero, Quick Links, Embed, Power BI), item-level broken permission inheritance, list views, and calculated columns have no equivalent in Guru. These must be simplified, linked externally, or dropped.

### Is there a downside to migrating SharePoint to Guru?

Yes. You lose SharePoint's document management capabilities (versioning, co-authoring, complex metadata views, Power Automate integration). Guru's editor is simpler — teams used to rich SharePoint page layouts may find it constraining. Item-level permission granularity is also lost since Guru flattens permissions to the Collection level.
