git:20260408.b387ecf to git:20260416.e328e9f

22 added, 1 removed. Audit A to A.

---
name: notion-cli-agent
description: Use the local Notion CLI (notion-cli-agent) to query, create, update, and manage Notion pages and databases via shell. Use when interacting with Notion workspaces, querying databases, creating or updating pages, managing tasks, reading content blocks, or running bulk/batch operations on Notion data. Prefer over Notion MCP or API calls.
---
# notion-cli-agent
Local CLI for full Notion access.
## Binary
```bash
notion <args> # globally installed via npm
```
- Auth: `NOTION_TOKEN` env var, or `~/.config/notion/api_key`.
+ ## Auth (read this — 401 is usually NOT an invalid token)
+
+ Token resolution order inside the CLI (`getTokenSync()` in `client.ts`):
+
+ 1. `NOTION_TOKEN` env var (highest priority)
+ 2. `NOTION_API_KEY` env var
+ 3. `~/.config/notion/api_key`
+ 4. `~/.notion/token`
+
+ **Gotcha:** Claude Code sessions often inherit a **stale `NOTION_TOKEN`** env var from a parent process (older sessions, MCP config, etc.). When that happens, `notion <cmd>` returns `401 API token is invalid` even though `~/.config/notion/api_key` contains a perfectly valid token — the CLI never reads the file because the env var wins.
+
+ **Diagnostic:** `notion doctor` will show `✅ Token: Token found` but `❌ API Connection: Token is invalid or expired`. That pattern = stale env var, NOT a bad file.
+
+ **Reliable workaround** (always works from Claude Code sessions):
+
+ ```bash
+ NOTION_TOKEN=$(cat ~/.config/notion/api_key) notion <cmd>
+ ```
+
+ This forces the CLI to use the file-backed token directly, bypassing the stale env var. Use this as the default invocation inside Claude sessions unless you've already verified `notion doctor` is fully green.
+
+ If you want to fix it permanently, the correct, source-of-truth token lives in `~/.config/notion/api_key`. Update `.zshrc`, `~/.mcp/global-config.json`, and any other places where `NOTION_TOKEN` is exported to match the file, then restart the session.
## Workspace sync (do this first)
```bash
notion sync # cache all databases locally
notion list # show cached databases
notion list --json # for parsing
```
After sync, use database names instead of UUIDs in ALL commands:
```bash
notion db query "Tasks" --limit 5 --llm
notion find "Tasks" "overdue" --llm
notion stats overview "Projects"
```
If `~/.config/notion/workspace.json` exists (from `sync` or the **notion-onboarding** skill), names resolve automatically. Falls back to UUIDs if no cache.
## Agent Workflow
1. **Sync workspace** — `notion sync` (once per session, or `notion list` if already synced)
2. **Understand schema** — `notion inspect context "Tasks"` or `notion inspect schema "Tasks" --llm`
3. **Query deterministically first** — prefer `db query --title`, `search --exact --db --first`, or `--llm` over fuzzy workspace-wide search when you know the target DB
4. **Write** with `--dry-run` first on bulk/batch ops, then confirm with user
## Core Commands
### Discover
```bash
notion sync # cache databases for name lookup
notion list # show cached databases
notion inspect ws --compact # all databases, names + ids
notion inspect schema "Tasks" --llm # property types + valid values
notion inspect context "Tasks" # workflow context + examples
notion ai prompt "Tasks" # DB-specific agent instructions
```
### Query
```bash
# Exact lookup in a known DB (deterministic — uses database query API)
notion db query "Tasks" --title "Known Page" --json
notion db query "Tasks" --limit 20 --llm # compact output
# Fuzzy search (workspace-wide, best-effort — Notion may miss long titles)
notion search "keyword" --limit 10
notion search "keyword" --db <db_id> --llm # filter by parent DB
notion search "short title" --exact --first --json # best-effort exact match
# Natural language
notion find "overdue tasks unassigned" -d <db_id> --llm
notion find "high priority" -d <db_id> --explain # preview filter, don't run
```
**For exact lookup by title in a known DB, always use `db query --title` — not `search --exact`.** Notion's search API is fuzzy and may miss pages with long or common-word titles.
### Read pages
```bash
notion page get <page_id> # properties
notion page get <page_id> --content # + content blocks
notion page get <page_id> --json # raw JSON
notion page read <page_id> # content as Markdown (native API, 1 call)
notion page read <page_id> --blocks # legacy block-by-block conversion
notion page read <page_id> -o page.md # save to file
notion ai summarize <page_id> # concise summary
notion ai extract <page_id> --schema "email,phone,date"
```
### Write pages
```bash
notion page create --parent <db_id> --title "Task Name"
notion page create --parent <db_id> --title "Task" --prop "Status:status=Todo" --prop "Priority:select=High"
notion page update <page_id> --prop "Status:status=Done"
notion page update <page_id> --clear-prop "Assignee" # type-aware clear
notion page update <page_id> --clear-prop "Tags" --clear-prop "Deadline"
notion page write <page_id> -f content.md # write Markdown to page
notion page write <page_id> -f doc.md --replace # replace all content
notion page edit <page_id> --at 3 --delete 2 # surgical block editing
notion page edit <page_id> --at 5 --markdown "New text" # insert at position
```
### Add blocks
```bash
notion block append <page_id> --text "Paragraph"
notion block append <page_id> --heading2 "Section" --bullet "Item 1" --bullet "Item 2"
notion block append <page_id> --todo "Action item"
```
### Batch (minimize tool calls)
```bash
notion batch --dry-run --data '[
{"op":"get","type":"page","id":"<page_id>"},
{"op":"create","type":"page","parent":"<db_id>","data":{"title":"New"}},
{"op":"update","type":"page","id":"<page_id2>","data":{"Status":"Done"}}
]'
notion batch --llm --data '[...]' # execute
```
### Bulk & maintenance
```bash
notion bulk update <db_id> --where "Status=Todo" --set "Status=In Progress" --dry-run
notion stats overview <db_id>
notion validate check <db_id> --check-dates --check-stale 30
notion dedup <db_id> # find duplicate pages
notion dedup <db_id> --fuzzy # include near-duplicates
notion dedup <db_id> --fix --strategy keep-largest --yes # archive duplicates
```
## Output flags
| Flag | Use for |
|------|---------|
| `--llm` | Compact, structured output for agents (`search`, `db query`, `find`, `batch`, `inspect schema/context`, `stats overview`, `relations backlinks`) |
| `--json` / `-j` | Raw JSON for parsing |
| `--csv` | CSV with headers (`db query`, `find`) |
| `--tsv` | Tab-separated (`db query`, `find`) |
| `--ids-only` | One ID per line for piping (`db query`, `search`, `find`) |
| (default) | Human-readable |
## Property type filters
`--filter-prop-type` is required for non-text properties:
```bash
notion db query <db_id> \
--filter-prop "Status" --filter-type equals \
--filter-value "Done" --filter-prop-type status
```
Types: `status` · `select` · `multi_select` · `number` · `date` · `checkbox` · `people` · `relation`
See `references/filters.md` for full operator reference.
## Property type hints for --prop
Auto-detection treats plain strings as `select`. Use `Key:type=Value` to force a type:
```bash
notion page update <id> --prop "Status:status=Done" # status, not select
notion page update <id> --prop "Notes:rich_text=Text" # rich_text, not select
notion page update <id> --prop "Owner:people=<user_id>" # people
```
## Rules
- Property values are usually **case-sensitive** — verify exact status/select values with `inspect context`
- Property names are matched more flexibly in `0.10.0` (`resolvePropertyName()` is case-insensitive and whitespace-tolerant), but still prefer the real schema labels for reliability
- Title property name varies per DB (`"Name"`, `"Título"`, `"Task"` — check state or schema)
- Prefer `db query --title "..."` or `search --db <id> --exact --first` when you know the DB; avoid fuzzy `search` for operational updates
- Use `--clear-prop` instead of fake empty values like `Owner:people=` or `Tags=`
- `--dry-run` before any bulk/batch write
- Confirm with user before destructive bulk operations
## References
- `references/filters.md` — all property types × filter operators with examples
- `references/batch-patterns.md` — batch workflows (multi-update, bulk status sweep, multi-get)
- `references/workflows.md` — agent workflow recipes (task triage, weekly review, project sync)
## Self-help
```bash
notion quickstart # full quick reference
notion <command> --help # per-command help
notion ai suggest <db_id> "what I want to do"
```