| name | notion-cli |
| description | Reference for using ntn (Notion CLI) to manage Notion pages, data sources, workers, and raw Notion API requests from the terminal. Use when the user mentions Notion pages, databases, workers, or syncs. |
Notion CLI (ntn)
OAuth into the OS keychain, workspace-scoped. For CI, export NOTION_API_TOKEN=ntn_… (PAT) — it overrides the keychain.
Authentication
ntn login
ntn login poll
ntn logout
ntn doctor
ntn update
One-shot a non-default workspace: NOTION_WORKSPACE_ID=<id> ntn ….
No-keychain hosts (Docker, CI, headless SSH): NOTION_KEYRING=0 ntn login writes plain JSON to auth.json.
Pages
ntn pages get <page-id>
ntn pages get <page-id> --json
ntn pages create --parent page:<id> --content "# Title"
ntn pages create --parent database:<id> < page.md
ntn pages create --parent data-source:<ds-id>
ntn pages update <page-id> --content "new md"
ntn pages update <page-id> --allow-deleting-content
ntn pages trash <page-id> --yes
--parent shape: page:<id> | database:<id> | data-source:<id>. All page commands accept --notion-version <ver> (or NOTION_API_VERSION).
Data sources
Notion databases hold one or more data sources; query operations target a DS, not the DB.
ntn datasources resolve <db-id>
ntn datasources query <ds-id>
ntn datasources query <ds-id> --limit 100 --start-cursor <c>
ntn datasources query <ds-id> -s "Name asc" -s "Priority desc"
ntn datasources query <ds-id> --filter '<json>'
Filter shape: see Notion API "Filter data source entries".
Files
ntn files create
ntn files list
ntn files get <upload-id>
Raw Notion API (ntn api)
Injects Authorization + Notion-Version. No body → GET; any body input → POST unless -X overrides.
ntn api v1/users/me
ntn api ls [--json]
ntn api v1/comments --help
ntn api v1/comments --spec -X POST
ntn api v1/comments --docs -X POST
ntn api v1/users/me --notion-version 2026-03-11
ntn --verbose api …
--verbose redacts Authorization. --unsafe-verbose disables redaction — never paste its output anywhere shared.
Inline request DSL
| Form | Meaning | Example |
|---|
path=value | Body field, string | parent[page_id]=abc123 |
path:=json | Body field, parsed JSON | archived:=true |
name==value | Query parameter | page_size==100 |
Header:Value | Request header | Accept:application/json |
Use = for strings, := for booleans/numbers/null/arrays/objects. Bracket notation handles keys with spaces or punctuation; [] appends, [N] sets explicit indexes; bracket and dot syntax both nest.
ntn api v1/pages \
parent[page_id]="$PARENT" \
properties.Name.title[0].text.content="Meeting notes"
ntn api "v1/pages/$ID" -X PATCH archived:=false properties[Priority][number]:=2
ntn api "v1/blocks/$ID/children" -X PATCH \
children[0][type]=heading_2 \
children[0][heading_2][rich_text][0][text][content]="Section header"
Body sources (pick one)
ntn api v1/pages < create-page.json
jq -n '...' | ntn api v1/pages
ntn api v1/search --data '{"query":"roadmap"}'
ntn api v1/pages parent[page_id]=…
Stdin / --data / inline-DSL are mutually exclusive. Headers and == query params combine with any one.
Workers
Workers package syncs / tools / webhooks. Worker resolution: --worker-id → positional <worker-id> → workerId in ./workers.json. Most subcommands accept --json (or --plain for TSV). Aliases throughout: list↔ls, delete↔rm, tui↔ui.
ntn workers new [dir]
ntn workers deploy
ntn workers deploy --name <n>
ntn workers deploy --local-build
ntn workers deploy --no-git
ntn workers list
ntn workers get [worker-id]
ntn workers create --name <n>
ntn workers delete [worker-id] --yes
ntn workers tui
ntn workers capabilities list
ntn workers exec <key>
ntn workers exec <key> -d '{"x":1}'
ntn workers exec <key> --stream
ntn workers exec <key> -l
ntn workers exec <key> -l --dotenv .env.local
Syncs (scheduled capabilities)
ntn workers sync status [<key>]
ntn workers sync trigger <key>
ntn workers sync trigger <key> --preview
ntn workers sync trigger <key> --context '<json>'
ntn workers sync trigger <key> -l
ntn workers sync pause <key>
ntn workers sync resume <key>
ntn workers sync state get <key>
ntn workers sync state reset <key>
Env vars (encrypted, write-only)
ntn workers env set FOO=bar BAZ=qux
ntn workers env list
ntn workers env unset FOO
ntn workers env pull
ntn workers env push
OAuth, runs, webhooks
ntn workers oauth start <key>
ntn workers oauth token <key>
ntn workers oauth show-redirect-url
ntn workers runs list
ntn workers runs logs <run-id>
ntn workers webhooks list [worker-id]
Flags & Environment
| Flag | Description |
|---|
-v, --verbose | Full error chains; on api, dumps request/response |
--workers-config-file <p> | Override workers.json lookup; its workspaceId selects workspace |
-V, --version / -h, --help | Standard |
| Variable | Purpose |
|---|
NOTION_API_TOKEN | PAT; takes precedence over the keychain |
NOTION_WORKSPACE_ID | One-shot workspace selector |
NOTION_KEYRING | 0 → file-based auth at auth.json |
NOTION_API_VERSION | Default Notion-Version for api |
NOTION_WORKERS_CONFIG_FILE | Same as --workers-config-file |
NOTION_HOME | Override config dir ($XDG_CONFIG_HOME/notion) |
Shell completions: ntn completions {bash|zsh|fish|powershell|elvish}.
Patterns
Page id from a URL
URL shape: https://www.notion.so/<workspace>/<slug>-<32-hex>?…. Strip everything but the trailing 32 hex chars:
PAGE_ID=$(echo "$URL" | grep -oE '[0-9a-f]{32}' | tail -n1)
ntn pages get "$PAGE_ID"
Database → page rows
datasources operate on data source IDs, not database IDs:
DS_ID=$(ntn datasources resolve "$DB_ID" --json | jq -r '.[0].id')
ntn datasources query "$DS_ID" --limit 100 --filter-file ./filter.json
Capture x-request-id for support
ntn --verbose api "v1/pages/$ID" -X PATCH archived:=true 2>&1 | tee /tmp/ntn-trace
grep -i 'x-request-id' /tmp/ntn-trace
Known Limitations
ntn login rejects guests and restricted members — admin must upgrade your role, or use a PAT.
workers env push reads ./.env by default — confirm --file before pushing against production secrets.
- Headless
ntn login sessions expire quickly; restart ntn login if poll fails.