Agent Notion
A TypeScript CLI tool that enables AI agents and humans to interact with Notion workspaces through the unofficial private API. Supports full CRUD operations on pages, databases, blocks, search, and user management.
Note: This skill uses Notion's internal/private API (
/api/v3/), which is separate from the official public API. For official API access, useagent-notionbot.
Quick Start
# 1. Extract token_v2 from Notion desktop app agent-notion auth extract # 2. Find your workspace ID agent-notion workspace list --pretty # 3. Search for a page agent-notion search "Roadmap" --workspace-id <workspace-id> --pretty # 4. Get page content agent-notion page get <page-id> --workspace-id <workspace-id> --pretty # 5. Query a database agent-notion database query <collection-id> --workspace-id <workspace-id> --pretty
Important:
--workspace-idis required for ALL commands that operate within a specific workspace. Useagent-notion workspace listto find your workspace ID.
Authentication
Token Extraction (Desktop App)
Extract token_v2 from the Notion desktop app automatically. No API keys or OAuth needed.
# Extract token_v2 from Notion desktop app agent-notion auth extract # Check auth status (shows extracted token_v2) agent-notion auth status # Remove stored token_v2 agent-notion auth logout
On macOS, your system may prompt for Keychain access — this is normal and required to decrypt the cookie.
The extracted token_v2 is stored at ~/.config/agent-notion/credentials.json with 0600 permissions.
Memory
The agent maintains a ~/.config/agent-notion/MEMORY.md file as persistent memory across sessions. This is agent-managed — the CLI does not read or write this file. Use the Read and Write tools to manage your memory file.
Reading Memory
At the start of every task, read ~/.config/agent-notion/MEMORY.md using the Read tool to load any previously discovered workspace IDs, page IDs, database IDs, and user preferences.
- •If the file doesn't exist yet, that's fine — proceed without it and create it when you first have useful information to store.
- •If the file can't be read (permissions, missing directory), proceed without memory — don't error out.
Writing Memory
After discovering useful information, update ~/.config/agent-notion/MEMORY.md using the Write tool. Write triggers include:
- •After discovering workspace IDs (from
workspace list) - •After discovering useful page IDs, database IDs, collection IDs (from
search,page list,page get,database list, etc.) - •After the user gives you an alias or preference ("call this the Tasks DB", "my main workspace is X")
- •After discovering page/database structure (parent-child relationships, what databases live under which pages)
When writing, include the complete file content — the Write tool overwrites the entire file.
What to Store
- •Workspace IDs with names
- •Page IDs with titles and parent context
- •Database/collection IDs with titles and parent context
- •User-given aliases ("Tasks DB", "Main workspace")
- •Commonly used view IDs
- •Parent-child relationships (which databases are under which pages)
- •Any user preference expressed during interaction
What NOT to Store
Never store token_v2, credentials, API keys, or any sensitive data. Never store full page content (just IDs and titles). Never store block-level IDs unless they're persistent references (like database blocks).
Handling Stale Data
If a memorized ID returns an error (page not found, access denied), remove it from MEMORY.md. Don't blindly trust memorized data — verify when something seems off. Prefer re-searching over using a memorized ID that might be stale.
Format / Example
Here's a concrete example of how to structure your MEMORY.md:
# Agent Notion Memory ## Workspaces - `abc123-...` — Acme Corp (default) ## Pages (Acme Corp) - `page-id-1` — Product Roadmap (top-level) - `page-id-2` — Q1 Planning (under Product Roadmap) ## Databases (Acme Corp) - `coll-id-1` — Tasks (under Product Roadmap, views: `view-1`) - `coll-id-2` — Contacts (top-level) ## Aliases - "roadmap" → `page-id-1` (Product Roadmap) - "tasks" → `coll-id-1` (Tasks database) ## Notes - User prefers --pretty output for search results - Main workspace is "Acme Corp"
Memory lets you skip repeated
searchandworkspace listcalls. When you already know an ID from a previous session, use it directly.
Commands
Auth Commands
agent-notion auth extract # Extract token_v2 from Notion desktop app agent-notion auth status # Check authentication status agent-notion auth logout # Remove stored token_v2
Page Commands
# List pages in a space (top-level only) agent-notion page list --workspace-id <workspace_id> --pretty agent-notion page list --workspace-id <workspace_id> --depth 2 --pretty # Get a page and all its content blocks agent-notion page get <page_id> --workspace-id <workspace_id> --pretty agent-notion page get <page_id> --workspace-id <workspace_id> --limit 50 agent-notion page get <page_id> --workspace-id <workspace_id> --backlinks --pretty # Create a new page under a parent agent-notion page create --workspace-id <workspace_id> --parent <parent_id> --title "My Page" --pretty # Create a page with markdown content agent-notion page create --workspace-id <workspace_id> --parent <parent_id> --title "My Doc" --markdown '# Hello\n\nThis is **bold** text.' # Create a page with markdown from a file agent-notion page create --workspace-id <workspace_id> --parent <parent_id> --title "My Doc" --markdown-file ./content.md # Replace all content on a page with new markdown agent-notion page update <page_id> --workspace-id <workspace_id> --replace-content --markdown '# New Content' agent-notion page update <page_id> --workspace-id <workspace_id> --replace-content --markdown-file ./updated.md # Update page title or icon agent-notion page update <page_id> --workspace-id <workspace_id> --title "New Title" --pretty agent-notion page update <page_id> --workspace-id <workspace_id> --icon "🚀" --pretty # Archive a page agent-notion page archive <page_id> --workspace-id <workspace_id> --pretty
Database Commands
# Get database schema
agent-notion database get <collection_id> --workspace-id <workspace_id> --pretty
# Query a database (auto-resolves default view)
agent-notion database query <collection_id> --workspace-id <workspace_id> --pretty
agent-notion database query <collection_id> --workspace-id <workspace_id> --limit 10 --pretty
agent-notion database query <collection_id> --workspace-id <workspace_id> --view-id <view_id> --pretty
agent-notion database query <collection_id> --workspace-id <workspace_id> --search-query "keyword" --pretty
agent-notion database query <collection_id> --workspace-id <workspace_id> --timezone "America/New_York" --pretty
# List all databases in workspace
agent-notion database list --workspace-id <workspace_id> --pretty
# Create a database
agent-notion database create --workspace-id <workspace_id> --parent <page_id> --title "Tasks" --pretty
agent-notion database create --workspace-id <workspace_id> --parent <page_id> --title "Tasks" --properties '{"status":{"name":"Status","type":"select"}}' --pretty
# Update database title or schema
agent-notion database update <collection_id> --workspace-id <workspace_id> --title "New Name" --pretty
# Add a row to a database
agent-notion database add-row <collection_id> --workspace-id <workspace_id> --title "Row title" --pretty
agent-notion database add-row <collection_id> --workspace-id <workspace_id> --title "Row title" --properties '{"Status":"In Progress","Due":{"start":"2025-03-01"}}' --pretty
# Get view configuration and property visibility
agent-notion database view-get <view_id> --workspace-id <workspace_id> --pretty
# Show or hide properties on a view (comma-separated names)
agent-notion database view-update <view_id> --workspace-id <workspace_id> --show "ID,Due" --pretty
agent-notion database view-update <view_id> --workspace-id <workspace_id> --hide "Assignee" --pretty
agent-notion database view-update <view_id> --workspace-id <workspace_id> --show "Status" --hide "Due" --pretty
Block Commands
# Get a specific block
agent-notion block get <block_id> --workspace-id <workspace_id> --pretty
agent-notion block get <block_id> --workspace-id <workspace_id> --backlinks --pretty
# List child blocks
agent-notion block children <block_id> --workspace-id <workspace_id> --pretty
agent-notion block children <block_id> --workspace-id <workspace_id> --limit 50 --pretty
agent-notion block children <block_id> --workspace-id <workspace_id> --start-cursor '<next_cursor_json>' --pretty
# Append child blocks
agent-notion block append <parent_id> --workspace-id <workspace_id> --content '[{"type":"text","properties":{"title":[["Hello world"]]}}]' --pretty
# Append markdown content as blocks
agent-notion block append <parent_id> --workspace-id <workspace_id> --markdown '# Hello\n\nThis is **bold** text.'
# Append markdown from a file
agent-notion block append <parent_id> --workspace-id <workspace_id> --markdown-file ./content.md
# Update a block
agent-notion block update <block_id> --workspace-id <workspace_id> --content '{"properties":{"title":[["Updated text"]]}}' --pretty
# Delete a block
agent-notion block delete <block_id> --workspace-id <workspace_id> --pretty
Block Types Reference
The internal API uses a specific block format. Here are all supported types:
Headings
{"type": "header", "properties": {"title": [["Heading 1"]]}}
{"type": "sub_header", "properties": {"title": [["Heading 2"]]}}
{"type": "sub_sub_header", "properties": {"title": [["Heading 3"]]}}
Text
{"type": "text", "properties": {"title": [["Plain text paragraph"]]}}
Lists
{"type": "bulleted_list", "properties": {"title": [["Bullet item"]]}}
{"type": "numbered_list", "properties": {"title": [["Numbered item"]]}}
To-Do / Checkbox
{"type": "to_do", "properties": {"title": [["Task item"]], "checked": [["Yes"]]}}
{"type": "to_do", "properties": {"title": [["Unchecked task"]], "checked": [["No"]]}}
Code Block
{"type": "code", "properties": {"title": [["console.log('hello')"]], "language": [["javascript"]]}}
Quote
{"type": "quote", "properties": {"title": [["Quoted text"]]}}
Divider
{"type": "divider"}
Rich Text Formatting
Rich text uses nested arrays with formatting codes:
| Format | Syntax | Example |
|---|---|---|
| Plain | [["text"]] | [["Hello"]] |
| Bold | ["text", [["b"]]] | ["Hello", [["b"]]] |
| Italic | ["text", [["i"]]] | ["Hello", [["i"]]] |
| Strikethrough | ["text", [["s"]]] | ["Hello", [["s"]]] |
| Inline code | ["text", [["c"]]] | ["Hello", [["c"]]] |
| Link | ["text", [["a", "url"]]] | ["Click", [["a", "https://example.com"]]] |
| Bold + Italic | ["text", [["b"], ["i"]]] | ["Hello", [["b"], ["i"]]] |
Multiple segments: [["plain "], ["bold", [["b"]]], [" more plain"]]
Comment Commands
# List comments on a page agent-notion comment list --page <page_id> --workspace-id <workspace_id> --pretty # Create a comment on a page (starts a new discussion) agent-notion comment create "This is a comment" --page <page_id> --workspace-id <workspace_id> --pretty # Reply to an existing discussion thread agent-notion comment create "Replying to thread" --discussion <discussion_id> --workspace-id <workspace_id> --pretty # Get a specific comment by ID agent-notion comment get <comment_id> --workspace-id <workspace_id> --pretty
Search Command
# Search across workspace (--workspace-id is required) agent-notion search "query" --workspace-id <workspace_id> --pretty agent-notion search "query" --workspace-id <workspace_id> --limit 10 --pretty agent-notion search "query" --workspace-id <workspace_id> --start-cursor <offset> --pretty
User Commands
# Get current user info agent-notion user me --pretty # Get a specific user agent-notion user get <user_id> --workspace-id <workspace_id> --pretty
Output Format
JSON (Default)
All commands output JSON by default for AI consumption:
# Search results agent-notion search "Roadmap" --workspace-id <workspace_id>
{
"results": [
{
"id": "305c0fcf-90b3-807a-bc1a-dc7cc18e0022",
"title": "Getting Started",
"score": 76.58
}
],
"has_more": true,
"next_cursor": "20",
"total": 100
}
# Database query — properties use human-readable field names from the collection schema agent-notion database query <collection_id> --workspace-id <workspace_id>
{
"results": [
{
"id": "row-uuid",
"properties": {
"Name": "Acme Corp",
"Status": "Active",
"Type": "Enterprise"
}
}
],
"has_more": false,
"next_cursor": null
}
# Page get — returns page metadata with content blocks agent-notion page get <page_id> --workspace-id <workspace_id>
{
"id": "page-uuid",
"title": "My Page",
"blocks": [
{ "id": "block-1", "type": "text", "text": "Hello world" },
{ "id": "block-2", "type": "to_do", "text": "Task item" }
]
}
# With --backlinks: includes pages that link to this page/block agent-notion page get <page_id> --workspace-id <workspace_id> --backlinks agent-notion block get <block_id> --workspace-id <workspace_id> --backlinks
{
"id": "page-uuid",
"title": "My Page",
"blocks": [...],
"backlinks": [
{ "id": "linking-page-uuid", "title": "Page That Links Here" }
]
}
# Block get — collection_view blocks include collection_id and view_ids agent-notion block get <block_id> --workspace-id <workspace_id>
{
"id": "block-uuid",
"type": "collection_view",
"text": "",
"parent_id": "parent-uuid",
"collection_id": "collection-uuid",
"view_ids": ["view-uuid"]
}
Pretty (Human-Readable)
Use --pretty flag for formatted output on any command:
agent-notion search "Roadmap" --workspace-id <workspace_id> --pretty
When to Use --backlinks
Backlinks reveal which pages/databases link to a given page. This is critical for efficient navigation.
Use --backlinks when:
- •Tracing relations: A search result looks like a select option, enum value, or relation target (e.g., a plan name or category). Backlinks instantly reveal all rows/pages that reference it via relation properties — no need to hunt for the parent database.
- •Finding references: You found a page and want to know what other pages mention or link to it.
- •Reverse lookups: Instead of querying every database to find rows pointing to a page, use backlinks on the target page to get them directly.
Example — finding who uses a specific plan:
# BAD: 15 API calls — search, open empty pages, trace parents, find database, query agent-notion search "Enterprise Plan" ... agent-notion page get <plan-page-id> ... # empty agent-notion block get <plan-page-id> ... # find parent # ... many more calls to discover the database # GOOD: 2-3 API calls — search, then backlinks on the target agent-notion search "Enterprise Plan" ... agent-notion page get <plan-page-id> --backlinks --pretty # → backlinks immediately show all people/rows linked to this plan
Pagination
Commands that return lists support pagination via has_more, next_cursor fields:
- •
block children: Cursor-based. Passnext_cursorvalue from previous response as--start-cursor. - •
search: Offset-based. Passnext_cursorvalue (a number) as--start-cursor. - •
database query: Use--limitto control page size.has_moreindicates more results exist, but the private API does not support cursor-based pagination — increase--limitto fetch more rows.
Limitations
- •
auth extractsupports macOS and Linux. Windows DPAPI decryption is not yet supported. - •
token_v2uses the unofficial internal API and may break if Notion changes it. - •This is a private/unofficial API and is not supported by Notion.