Use this skill to read and, with explicit confirmation, write Notion content through the Notion API: pages, database queries, search, and page property updates. This is a tool skill for the Notion platform. Building Notion integrations, writing complex block compositions, or building an app on the Notion API is application development; this skill owns the everyday agent workflow: finding the right page, answering from a database, and making a confirmed edit.
Operating contract
- Read-only discovery before any mutation. Retrieve pages, query databases, and search freely. The bundled
notion-cliscript makes reads without writing anything. - Confirm the target, scope, and rollback path before acting. Creating a page or updating properties changes a shared workspace that teammates read. Both require an explicit human directive plus
--dry-runpreview and--yesconfirmation throughnotion-cli. Property updates overwrite existing values — state the current value and the replacement before confirming. - Respect bounded reads. Notion paginates with
page_sizeandhas_more; never page past what the task needs.notion-cli --limitcaps every search and query. - Keep evidence bounded. Quote short page titles, property values, and IDs; never paste full pages, tokens, or raw API payloads into chat.
- Know the API version. The
Notion-Versionheader pins the API contract; reads that work today can change with a version bump.notion-clisends2022-06-28by default and honorsNOTION_VERSION.
The notion-cli script
scripts/notion-cli is an agent-first, stdlib-only CLI over the Notion API. It covers the full issue scope: pages, databases (query), search, and updates.
notion/scripts/notion-cli --help # no token or network needed
notion/scripts/notion-cli --json pages get --page-id <page>
# Read only properties you plan to change; absent names are marked explicitly
notion/scripts/notion-cli --json pages get --page-id <page> --property Status --property Reviewer
notion/scripts/notion-cli --json --limit 10 databases query --database-id <db>
notion/scripts/notion-cli --json search query --query "on-call runbook"
notion/scripts/notion-cli pages update --page-id <page> --properties props.json --dry-run
notion/scripts/notion-cli pages update --page-id <page> --properties props.json --yes
notion/scripts/notion-cli pages create --parent-database <db> --title "New row" --yes
Exit codes: 0 success, 1 API error or failed check, 2 usage error. Creates and updates are guarded: without --dry-run or --yes the script refuses with exit 1 and never calls the API. Reads are bounded by --limit (default 20, max 100).
Operating loop
- Locate the content:
search queryto find pages and databases by text, or a known ID directly. - Read with bounds:
pages getfor a single page,databases queryfor rows in a database (optionally with a JSON--filter), always capped by--limit. - Triage the answer: map the question to evidence (page title + properties, database rows, search results with
has_morestate). - Act with confirmation: only a human directive to change, previewed with
--dry-runand confirmed with--yes. - Verify: re-read the page (
pages get) and confirm the property values landed.
Pages, databases, search
- Pages (
GET /pages/{id}): a page is an ID, a title (extracted from thetitleorNameproperty), a URL, timestamps, and typed property summaries. JSON output includes each property's type, value, and completeness;--property NAMEmay be repeated to select properties and marks a requested absent property aspresent: false. Human output marks incomplete properties as<incomplete: reason>. Long text also includes a truncation marker and original character count, so it cannot be mistaken for a verified full value. - Databases (
POST /databases/{id}/query): query rows as pages with apage_sizecap and an optional structured--filterJSON file (e.g.{"property": "Status", "select": {"equals": "Done"}}).has_moretells you whether the cap hid further rows. - Search (
POST /search): finds pages and databases by text across the integration's accessible workspace; results are bounded by--limit. - Updates (
PATCH /pages/{id}): property updates overwrite values (select, status, checkbox, rich text, etc.). Preview the exact properties payload with--dry-runand confirm with--yes; verify with a follow-uppages get.
Integration access model
- Notion integrations authenticate with a bot-style token (
secret_...) and can only see the pages and databases explicitly shared with the integration. A page that exists in the workspace but is not shared returns 404/object_not_found— that is an access-model result, not a missing page. - The
Notion-Versionheader selects the API contract. The CLI defaults to2022-06-28; setNOTION_VERSIONwhen a workspace or application pins a different version. - Tokens are workspace-scoped credentials. Store them in the environment (
NOTION_TOKEN), never in code, chat, or commits. Revoke a leaked integration token in the Notion integration settings.
Reference routing
| Load when | Reference |
|---|---|
| Sources, version notes, refresh procedure | references/00-source-index.md |
| API endpoints, pagination, property types, filters, and error handling | references/01-api-operations.md |
Included artifacts
scripts/notion-cli: bounded, stdlib-only CLI (pages get/create/update, databases query, search;--json;--limit; mutations gated by--dry-run/--yes).tests/test_notion_cli.py: 21 deterministic tests against a stub Notion API, including typed property read-back, explicit absent/truncated states, pagination failures, the mutation gate, and the read-only contract.references/: dated source index + API operations reference.evals/evals.json: six output-quality evaluation cases for agent runs.
Verification boundary
| Claim | Minimum evidence |
|---|---|
| A page exists and its title | notion-cli pages get --page-id ... --json returns the title and ID |
| A database query answered the question | notion-cli databases query --json returns bounded rows with has_more state |
| Search found the content | notion-cli search query --json returns the matching page/database with ID and title |
| An update landed | notion-cli pages update --yes exits 0 and a follow-up pages get --property NAME shows each changed property's typed value; compare it with the requested value |
| A mutation is safe to run | notion-cli ... --dry-run prints the exact payload that would be sent |
Hard boundaries
- Never create or update a page without a human directive,
--dry-runpreview, and--yesconfirmation — Notion edits are visible to everyone with access to the page. - Never claim a page is missing when it may simply not be shared with the integration; verify the access model first.
- Never page reads past
--limit; never dump full pages, tokens, or raw payloads into chat. - This skill operates the Notion API. It does not build Notion integrations (application development) or cover other knowledge-base products.
When not to use
- Building Notion integrations or apps (OAuth flows, custom blocks, public API products, block-tree composition beyond property updates) — that is application development; see backend-engineering for service design.
- Other knowledge bases and document tools (Confluence, Google Docs, wikis) — each has its own tooling; this skill covers Notion only.
- Workspace administration (user management, workspace settings, integration approval) — that is the Notion admin console.