Skip to main content

CLI Commands

ntnsync provides commands for syncing Notion content to a local git repository.

Global Flags

FlagEnv VarDescription
--tokenNOTION_TOKENNotion API token (required)
--store-path, -sNTN_DIRGit repository path (default: notion)
--verboseEnable debug logging

Logging Environment Variables

VariableDefaultDescription
NTN_LOG_FORMATtextLog output format: text (human-readable) or json (structured)

NTN_LOG_FORMAT: Controls log output format.

  • text (default): Human-readable text format suitable for development
  • json: JSON-formatted structured logs for CI/CD, log aggregation, and monitoring

Examples:

# Default text format
./ntnsync sync -v

# JSON format for CI/CD pipelines
NTN_LOG_FORMAT=json ./ntnsync sync -v

JSON output example:

{"time":"2026-01-24T10:30:45Z","level":"INFO","msg":"Starting sync"}
{"time":"2026-01-24T10:30:46Z","level":"DEBUG","msg":"Processing page","page_id":"abc123"}

Performance Environment Variables

VariableDefaultDescription
NTN_BLOCK_DEPTH0Maximum depth for block discovery (0 = unlimited)
NTN_QUEUE_DELAY0Delay between processing queue files (e.g., 5s, 1m)
NTN_MAX_FILE_SIZE5MBMaximum file size to download

NTN_BLOCK_DEPTH: Limits how deeply nested blocks are fetched.

  • 0 (default): Fetch all nested blocks (unlimited depth)
  • Positive integer: Stop exploring at that depth level
  • When limited, adds simplified_depth: N to page frontmatter

Examples:

# Limit to 3 levels of nesting
NTN_BLOCK_DEPTH=3 ./ntnsync sync

# Fast sync with shallow block fetching
NTN_BLOCK_DEPTH=2 ./ntnsync sync --max-pages 100

Commit/Push Environment Variables

Git commit and push behavior is controlled via environment variables:

VariableDefaultDescription
NTN_COMMITfalseEnable automatic git commit after changes
NTN_COMMIT_PERIOD0Commit periodically during sync (e.g., 30s, 1m, 5m)
NTN_PUSHautoPush to remote after commits

NTN_COMMIT: Set to true, 1, or yes to enable commits.

NTN_COMMIT_PERIOD: When set to a duration (e.g., 1m), commits are made periodically during long sync operations. This also implicitly enables NTN_COMMIT.

NTN_PUSH: Controls whether to push after commits.

  • Defaults to true when NTN_GIT_URL is set (remote mode)
  • Defaults to false when NTN_GIT_URL is not set (local mode)
  • Can be explicitly set to true to push to local repo's configured remote
  • Set to false to commit locally without pushing

Examples:

# Commit and push (when NTN_GIT_URL is set)
NTN_COMMIT=true ./ntnsync sync

# Commit but don't push
NTN_COMMIT=true NTN_PUSH=false ./ntnsync sync

# Periodic commits during long sync
NTN_COMMIT_PERIOD=1m ./ntnsync sync

Root Page Configuration

Root pages are configured in root.md at the repository root. This file uses a task list format with interactive checkboxes:

# Root Pages

- [x] **tech**: https://notion.so/Wiki-2c536f5e48f44234ad8d73a1a148e95d
- [x] **product**: https://notion.so/Product-Specs-abc123def456
- [ ] **archive**: https://notion.so/Old-Docs-disabled123

Entry format: - [x] **folder**: url

  • Checkbox ([x] enabled, [ ] disabled) - clickable in GitHub
  • **folder**: Target folder name for the root page and its children
  • url: Notion page or database URL

Behavior:

  • On every command (pull, sync, list, status), root.md is reconciled with registries
  • Disabled roots ([ ]) are skipped during pull and sync
  • Duplicate page IDs are automatically removed
  • File is created with template if it doesn't exist

Commands

get

Fetch a single page without marking it as root.

ntnsync get <page_id_or_url> [--folder FOLDER]
FlagDefaultDescription
--folder, -fauto-detectTarget folder (optional)

Behavior:

  • Fetches single page with is_root: false
  • Auto-detects folder by tracing parent chain to existing root
  • Fetches missing parent pages recursively
  • Places page in correct hierarchy location
  • Queues child pages

Use cases:

  • Fetch specific page deep in hierarchy
  • Recover a deleted page
  • Add page that's part of existing tree

scan

Re-scan a page to discover all children.

ntnsync scan <page_id_or_url>

Behavior:

  • Re-scans existing page for all child pages
  • Finds pages not yet tracked locally
  • Queues new children with type init
  • Reports statistics (total, new, already tracked)

Use cases:

  • Discover pages added after initial sync
  • Re-scan after reorganizing in Notion
  • Ensure all descendants are tracked

pull

Queue changed pages for syncing.

ntnsync pull [options]
FlagDefaultDescription
--folder, -fallFilter to specific folder
--since, -slast pullTime override (e.g., 24h, 7d, 30d)
--max-pages, -n0Limit pages queued (0 = unlimited)
--allfalseInclude undiscovered pages
--dry-runfalsePreview without modifying
--verbosefalseDetailed logging

Behavior:

  • Fetches pages changed since last pull
  • Queues them with type update and timestamps
  • Stores last_pull_time in state.json
  • Default mode: checks only tracked pages
  • --all mode: discovers new accessible pages
  • Stops early when reaching oldest_pull_result

Note: First pull requires --since flag (no previous pull time).

Examples:

ntnsync pull --since 24h --folder tech
ntnsync pull --all --max-pages 100 --dry-run
ntnsync pull -s 7d -n 500

sync

Process the queue and download pages.

ntnsync sync [options]
FlagDefaultDescription
--folder, -fallSync only specific folder
--max-pages, -n0Max pages to process (0 = unlimited)
--max-files, -w0Max markdown files to write (0 = unlimited)
--max-time, -t0Duration limit (e.g., 30s, 5m, 1h)
--stop-afterAlias for --max-time
--max-queue-files, -q0Max queue files to process

Behavior:

  • Processes queue entries in .notion-sync/queue/
  • Downloads pages recursively
  • Fetches parents first for proper structure
  • Type init: skips if exists and current
  • Type update: compares timestamps, skips unchanged
  • Remaining queue entries stay for next sync
  • Creates git commit if NTN_COMMIT=true
  • Commits periodically if NTN_COMMIT_PERIOD is set

Examples:

ntnsync sync --max-pages 100
ntnsync sync --folder tech -t 10m
NTN_COMMIT=true ntnsync sync -n 50 -w 20
NTN_COMMIT_PERIOD=1m ntnsync sync # Periodic commits during long sync

list

List folders and pages.

ntnsync list [--folder FOLDER] [--tree]
FlagDefaultDescription
--folder, -fallList specific folder only
--treefalseShow hierarchical structure

Output:

  • Lists all folders and their pages
  • Shows root page count, total pages, orphaned count
  • --tree shows parent-child hierarchy

status

Show sync status and queue statistics.

ntnsync status [--folder FOLDER]
FlagDefaultDescription
--folder, -fallShow specific folder status

Output:

  • Folder and page counts
  • Last sync time
  • Queue statistics (pending pages by type and folder)
  • Queue file details

cleanup

Delete orphaned pages not tracing to root.md.

ntnsync cleanup [--dry-run]
FlagDefaultDescription
--dry-runfalsePreview only, don't delete anything

Behavior:

  • Reconciles root.md first
  • Lists all page registries
  • Traces each page to its root
  • Deletes pages whose root is not in root.md
  • Removes both markdown files and registry files

Use cases:

  • Clean up after removing entries from root.md
  • Remove orphaned pages from reorganization
  • Prune pages from deleted root hierarchies

Examples:

ntnsync cleanup --dry-run    # Preview what would be deleted
ntnsync cleanup # Delete orphaned pages

reindex

Rebuild registry files from markdown files.

ntnsync reindex [--dry-run]
FlagDefaultDescription
--dry-runfalsePreview changes without modifying

Behavior:

  • Scans all markdown files recursively
  • Parses frontmatter for metadata
  • Rebuilds .notion-sync/ids/page-{id}.json registries
  • Handles duplicates by keeping latest last_edited
  • Deletes older duplicate files
  • Normalizes page IDs

Use cases:

  • Recover from deleted registry files
  • Fix corrupted registry data
  • Clean up duplicate pages
  • Rebuild after manual file edits

remote

Manage remote git repository configuration.

ntnsync remote show
ntnsync remote test

Subcommands:

SubcommandDescription
showDisplay current remote configuration from environment variables
testTest connection to remote repository

Environment Variables:

VariableDescription
NTN_GIT_URLRemote git repository URL (HTTPS or SSH)
NTN_GIT_PASSGit password/token for HTTPS authentication
NTN_GIT_BRANCHBranch name (default: main)
NTN_GIT_USERGit commit author name (default: ntnsync)
NTN_GIT_EMAILGit commit author email (default: ntnsync@localhost)
NTN_STORAGEStorage mode: local or remote (auto-detected from NTN_GIT_URL)

Examples:

# Show current configuration
ntnsync remote show

# Test connection to remote
NTN_GIT_URL=https://github.com/user/docs.git NTN_GIT_PASS=$TOKEN ntnsync remote test

serve

Start a webhook server to receive Notion events for real-time sync.

ntnsync serve [options]
FlagEnv VarDefaultDescription
--port, -pNTN_WEBHOOK_PORT8080HTTP port to listen on
--secretNTN_WEBHOOK_SECRETWebhook secret for signature verification
--pathNTN_WEBHOOK_PATH/webhooks/notionWebhook endpoint path
--auto-syncNTN_WEBHOOK_AUTO_SYNCtrueAutomatically sync after receiving events
--sync-delayNTN_WEBHOOK_SYNC_DELAY0Debounce delay before processing (e.g., 5s)

Behavior:

  • Listens for Notion webhook events
  • Queues changed pages when events arrive
  • Automatically triggers sync if --auto-sync is enabled
  • Verifies webhook signatures when --secret is configured
  • Uses debouncing with --sync-delay to batch rapid changes

Security:

  • Always configure --secret in production for signature verification
  • Without a secret, any request can trigger syncs

Examples:

# Start webhook server on port 8080
ntnsync serve

# With signature verification and custom port
ntnsync serve --port 3000 --secret $WEBHOOK_SECRET

# With debounce delay for batching changes
ntnsync serve --sync-delay 10s

# Disable auto-sync (queue only)
ntnsync serve --auto-sync=false

Webhook Environment Variables

VariableDefaultDescription
NTN_WEBHOOK_PORT8080HTTP port for webhook server
NTN_WEBHOOK_SECRETSecret for signature verification
NTN_WEBHOOK_PATH/webhooks/notionWebhook endpoint path
NTN_WEBHOOK_AUTO_SYNCtrueAuto-sync after receiving events
NTN_WEBHOOK_SYNC_DELAY0Debounce delay before processing

Typical Workflows

First-time sync

# 1. Create root.md with your root pages
cat > root.md << 'EOF'
# Root Pages

- [x] **tech**: https://www.notion.so/Wiki-2c536f5e...
EOF

# 2. Pull to queue all pages (use --since for first pull)
ntnsync pull --since 30d

# 3. Sync the queue (with commit)
NTN_COMMIT=true ntnsync sync

Incremental updates

# 1. Pull changes since last sync
ntnsync pull --folder tech

# 2. Sync the queue (with commit and push)
NTN_COMMIT=true ntnsync sync --folder tech

Add specific page to existing tree

# Auto-detects folder from parent chain
ntnsync get https://www.notion.so/SpecificPage-abc123

# Sync the page and its children (with commit)
NTN_COMMIT=true ntnsync sync

CI/CD automated sync

# Set environment variables for automated operation
export NTN_COMMIT=true
export NTN_GIT_URL=https://github.com/user/docs.git
export NTN_GIT_PASS=$GITHUB_TOKEN

# Pull and sync - commits and pushes automatically
ntnsync pull --since 2h
ntnsync sync

Real-time sync with webhooks

# Set up environment
export NOTION_TOKEN=secret_xxx
export NTN_COMMIT=true
export NTN_GIT_URL=https://github.com/user/docs.git
export NTN_GIT_PASS=$GITHUB_TOKEN
export NTN_WEBHOOK_SECRET=$WEBHOOK_SECRET

# Start webhook server
ntnsync serve --port 8080 --sync-delay 5s

Configure your Notion integration to send webhooks to https://your-server:8080/webhooks/notion.