Skip to main content
Import your Notion workspace into the Moxn Knowledge Base. You can import directly from the web app, or use the @moxn/kb-migrate CLI for more control.

Web Import

The fastest way to import from Notion — no terminal required.
  1. Sign in to moxn.dev and go to Knowledge Base
  2. Click ImportFrom Notion
  3. Connect your Notion account when prompted
  4. Select the pages and databases you want to import
  5. Click Next to start the import
Import menu showing Notion option
Notion import content selection dialog
The web importer handles authentication, content selection, and upload in one flow. Imported pages appear in your Knowledge Base immediately.

CLI Import

For scripting, CI pipelines, or more granular control over the import, use the @moxn/kb-migrate CLI. It extracts pages, converts blocks into sections, downloads images, and recreates Notion databases as KB databases with tag-backed columns.

Prerequisites

  • Node.js 18+
  • A Notion integration token with access to the pages you want to import
  • A Moxn API key with read and write permissions (create one at Settings > API Keys in the web app)

Step 1: Create a Notion Integration

Moxn reads your Notion content through a Notion integration. You need to create one and grant it access to your workspace.
  1. Go to notion.so/my-integrations and click New integration
  2. Name it (e.g. “Moxn Importer”), select your workspace, and click Submit
  3. Copy the Internal Integration Secret — this is your NOTION_TOKEN
  4. In Notion, open the top-level page you want to import
  5. Click the menu in the top right, then Connections > Connect to and select your integration
The integration can only see pages it has been explicitly connected to. If you want to import your entire workspace, connect the integration at the top-level page and it will inherit access to all child pages.

Step 2: Get Your Moxn API Key

  1. Open the Moxn web app and go to Settings > API Keys
  2. Create a new key with read/write scope
  3. Copy the key — this is your MOXN_API_KEY

Step 3: Run the Import

npx @moxn/kb-migrate notion \
  --token=YOUR_NOTION_TOKEN \
  --api-key=YOUR_MOXN_API_KEY
That’s it. The tool discovers all pages in your workspace, converts them to KB documents, and uploads them.
You can set environment variables instead of passing flags every time:
export NOTION_TOKEN="secret_abc123..."
export MOXN_API_KEY="moxn_xyz789..."
npx @moxn/kb-migrate notion

Preview First with Dry Run

See what would be imported without making changes:
npx @moxn/kb-migrate notion --dry-run

Import a Subtree

Import only a specific page and its children instead of the entire workspace:
npx @moxn/kb-migrate notion --root-page-id=abc123def456
The --root-page-id is the ID from the Notion page URL (the 32-character hex string after the page title).

Choose a Base Path

By default, documents are created under /imported-from-notion. Change this with --base-path:
npx @moxn/kb-migrate notion --base-path=/engineering

How It Works

The migration runs in two passes:

1. Discover

The tool searches your Notion workspace to find all pages and databases. It builds a tree of parent-child relationships and computes KB paths from page titles.

2. Extract

For each page, the tool fetches all blocks and converts them to Moxn KB sections:
Notion BlockMoxn Result
H2 headingSection boundary — a new KB section starts here
H1, H3 headingsMarkdown heading within the section
ParagraphsText block with formatting (bold, italic, code, links)
Bullet/numbered listsMarkdown lists with nesting
Code blocksCode block with language
ImagesDownloaded and uploaded to Moxn storage
TablesMarkdown table
CalloutsBlockquote with emoji prefix
Toggle blocksCollapsible content
Synced blocksResolved inline (with cycle detection)
Page mentionsInternal document links
Content before the first H2 heading goes into an “Introduction” section.

3. Upload

Each extracted document is created via the Moxn API. The tool handles conflicts based on the --on-conflict option and continues processing if individual pages fail.

4. Databases

After all pages are imported, the tool creates KB databases from Notion databases:
  • Select and Multi-select properties become KB database columns
  • Each option becomes a tag (e.g. a “Status” select with options “Done” and “In Progress” creates tags at /imported/db-name/status/done and /imported/db-name/status/in-progress)
  • Database entries (Notion pages within a database) are linked to the KB database
  • Tag values from Notion properties are assigned to the corresponding documents
Other property types (text, number, date, etc.) are not mapped to columns — they appear as text content within the page body.

Document Path Mapping

The tool creates KB paths from the Notion page hierarchy:
Notion Structure--base-pathKB Document Path
”API Guide” (top-level)/imported-from-notion/imported-from-notion/api-guide
”Setup” under “API Guide”/engineering/engineering/api-guide/setup
Database entry “Sprint 1”/engineering/engineering/sprint-board/sprint-1
Page titles are slugified: lowercased, spaces become hyphens, special characters removed.

CLI Options

OptionDefaultDescription
--token <token>$NOTION_TOKENNotion integration token (required)
--api-key <key>$MOXN_API_KEYMoxn API key (required)
--api-url <url>https://moxn.devMoxn API base URL
--base-path <path>/imported-from-notionPath prefix for all imported documents
--root-page-id <id>(entire workspace)Import only a subtree starting from this Notion page
--max-depth <n>(unlimited)Maximum page nesting depth
--on-conflict <action>skipWhat to do when a path already exists: skip or update
--default-permission <perm>Server defaultDefault permission: edit, read, or none
--ai-access <perm>Server defaultAI/MCP access level: edit, read, or none
--visibility <vis>(none)Shorthand: team (sets --default-permission=read) or private (sets --default-permission=none)
--dry-runfalsePreview changes without calling the API
--jsonfalseOutput results as JSON

Setting Permissions

Control who can see imported documents:
# Team can read, AI assistants can read
npx @moxn/kb-migrate notion --visibility=team --ai-access=read

# Private — only the importer can see them
npx @moxn/kb-migrate notion --visibility=private --ai-access=none
Flageditreadnone
--default-permissionTeam members can editTeam members can view onlyHidden from team
--ai-accessAI assistants can read and editAI assistants can read onlyHidden from AI
The --visibility flag is a shorthand:
  • --visibility=team sets --default-permission=read
  • --visibility=private sets --default-permission=none
See Permission Model for details on how permissions cascade.

Handling Conflicts

When you re-run the import and documents already exist at the target paths: --on-conflict=skip (default) — existing documents are left untouched:
npx @moxn/kb-migrate notion --on-conflict=skip
# First run: creates all documents
# Second run: skips all (already exist)
--on-conflict=update — existing documents are replaced with the latest Notion content:
npx @moxn/kb-migrate notion --on-conflict=update
# Creates new versions — previous versions are preserved in commit history

JSON Output

Use --json for structured output in CI or scripts:
npx @moxn/kb-migrate notion --json 2>/dev/null
{
  "timestamp": "2026-02-11T12:00:00.000Z",
  "source": { "type": "notion", "location": "Notion workspace" },
  "targetApi": "https://moxn.dev",
  "basePath": "/imported-from-notion",
  "options": { "dryRun": false, "onConflict": "skip" },
  "results": [
    {
      "sourcePath": "api-guide",
      "documentPath": "/imported-from-notion/api-guide",
      "status": "created",
      "sectionsCount": 5,
      "duration": 2100
    }
  ],
  "summary": {
    "total": 12,
    "created": 12,
    "updated": 0,
    "skipped": 0,
    "failed": 0,
    "duration": 18500
  }
}

Examples

Import entire workspace

npx @moxn/kb-migrate notion \
  --token=$NOTION_TOKEN \
  --api-key=$MOXN_API_KEY \
  --base-path=/from-notion

Import a single team’s pages

npx @moxn/kb-migrate notion \
  --root-page-id=abc123def456 \
  --base-path=/engineering \
  --visibility=team

Re-sync after Notion updates

npx @moxn/kb-migrate notion \
  --on-conflict=update \
  --base-path=/from-notion

Import with restricted AI access

npx @moxn/kb-migrate notion \
  --base-path=/internal \
  --default-permission=read \
  --ai-access=none

Limitations

ConstraintDetails
Max documents10,000 per import. Use --root-page-id for large workspaces.
Database propertiesOnly Select and Multi-select are mapped to KB columns. Other types appear as page content.
Embedded contentVideos, embeds, and bookmarks are converted to text links.
Synced blocksResolved inline with cycle detection. Deeply nested synced blocks may not render identically.
Rate limitsNotion API allows ~3 requests/second. The tool throttles automatically.

Troubleshooting

Pass your token via --token or set the environment variable:
export NOTION_TOKEN=secret_abc123...
npx @moxn/kb-migrate notion
The Notion integration can only access pages it’s been connected to. In Notion, open the parent page, click > Connections > Connect to and select your integration. Child pages inherit access from their parent.
Notion-hosted images are downloaded and re-uploaded to Moxn storage during import. If the download fails (e.g. expired URL), the image is skipped with a warning. Re-running the import with --on-conflict=update will retry failed images.External image URLs are kept as-is and must be publicly accessible.
Only Select and Multi-select Notion properties are imported as KB database columns. Other property types (text, number, date, formula, relation, etc.) are rendered as text in the page body instead.
The Notion API has rate limits (~3 requests/second). For a workspace with 100+ pages, expect the import to take a few minutes. The tool shows progress as it goes.Use --root-page-id to import a smaller subtree if you don’t need the entire workspace.
The tool continues processing remaining pages when one fails. Check the summary output for failed pages and their error messages. Re-run with --on-conflict=skip to retry only the failed ones (successfully created pages will be skipped).

Next Steps