feat(skills/notion): overhaul for Notion Developer Platform (May 2026) (#26612)

* feat(skills/notion): overhaul for Notion Developer Platform (May 2026)

Notion shipped its Developer Platform on May 13, 2026: ntn CLI, Workers,
Markdown API, bidirectional webhooks, agent tools. The existing skill only
covered curl + integration token CRUD, so it didn't surface any of the new
ergonomics — particularly the /markdown endpoints (much easier for agents
to consume) and the ntn CLI for headless API + Workers management.

This rewrite (v1.0.0 -> v2.0.0):

- Splits setup into Path A (HTTP, cross-platform incl. Windows), Path B
  (ntn CLI on macOS/Linux, with NOTION_API_TOKEN env var for headless),
  and Path C (Windows fallback — HTTP API or WSL2; native ntn is 'coming
  soon').
- Keeps the full curl reference (still the only Windows-compatible path).
- Adds /markdown endpoints — GET and PATCH page-as-markdown, plus POST
  /v1/pages with a markdown body param. Agent-friendly, no CLI required.
- Adds ntn CLI cheat sheet for raw API shorthand, file uploads, and
  workspace flags.
- Adds Notion Workers section: scaffold, tool/webhook capability shapes,
  lifecycle commands. Gated on Business/Enterprise plans + macOS/Linux.
- Adds Notion-flavored Markdown reference (callouts, toggles, columns,
  mentions, colors) for the /markdown endpoints.
- Adds a 'choose the right path' decision table at the bottom.
- Notes the new efficient Notion MCP server as an optional wiring path.

Auto-generated docs page regenerated via
website/scripts/generate-skill-docs.py.

* docs(skills-catalog): update notion description for v2.0.0
This commit is contained in:
Teknium 2026-05-15 14:58:23 -07:00 committed by GitHub
parent 887ba1fb03
commit 42070ecefb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 632 additions and 80 deletions

View file

@ -1,35 +1,158 @@
--- ---
name: notion name: notion
description: "Notion API via curl: pages, databases, blocks, search." description: "Notion API + ntn CLI: pages, databases, markdown, Workers."
version: 1.0.0 version: 2.0.0
author: community author: community
license: MIT license: MIT
platforms: [linux, macos, windows] platforms: [linux, macos, windows]
metadata:
hermes:
tags: [Notion, Productivity, Notes, Database, API]
homepage: https://developers.notion.com
prerequisites: prerequisites:
env_vars: [NOTION_API_KEY] env_vars: [NOTION_API_KEY]
metadata:
hermes:
tags: [Notion, Productivity, Notes, Database, API, CLI, Workers]
homepage: https://developers.notion.com
--- ---
# Notion API # Notion
Use the Notion API via curl to create, read, update pages, databases (data sources), and blocks. No extra tools needed — just curl and a Notion API key. Talk to Notion two ways. Same integration token works for both — pick by what's available.
## Prerequisites **`ntn` CLI** — Notion's official CLI. Shorter syntax, one-line file uploads, required for Workers. macOS + Linux only as of May 2026 (Windows support "coming soon"). **Default when installed.**
**HTTP + curl** — works everywhere including Windows. **Default fallback** when `ntn` isn't installed.
## Setup
### 1. Get an integration token (required for both paths)
1. Create an integration at https://notion.so/my-integrations 1. Create an integration at https://notion.so/my-integrations
2. Copy the API key (starts with `ntn_` or `secret_`) 2. Copy the API key (starts with `ntn_` or `secret_`)
3. Store it in `~/.hermes/.env`: 3. Store in `~/.hermes/.env`:
``` ```
NOTION_API_KEY=ntn_your_key_here NOTION_API_KEY=ntn_your_key_here
``` ```
4. **Important:** Share target pages/databases with your integration in Notion (click "..." → "Connect to" → your integration name) 4. **Share target pages/databases with the integration** in Notion: page menu `...``Connect to` → your integration name. Without this, the API returns 404 for that page even though it exists.
### 2. Install `ntn` (preferred path on macOS / Linux)
```bash
# Recommended
curl -fsSL https://ntn.dev | bash
# Or via npm (needs Node 22+, npm 10+)
npm install --global ntn
ntn --version # verify
```
**Skip `ntn login` — use the integration token instead.** This works headlessly, no browser needed:
```bash
export NOTION_API_TOKEN=$NOTION_API_KEY # ntn reads NOTION_API_TOKEN
export NOTION_KEYRING=0 # don't try to use the OS keychain
```
Add those exports to your shell profile (or to `~/.hermes/.env`) so every session inherits them.
### 3. Choose path at runtime
```bash
if command -v ntn >/dev/null 2>&1; then
# use ntn
else
# fall back to curl
fi
```
Windows users: skip step 2 entirely until native `ntn` ships — Path B works fine. If you want CLI ergonomics now, install `ntn` inside WSL2.
## API Basics ## API Basics
All requests use this pattern: `Notion-Version: 2025-09-03` is required on all HTTP requests. `ntn` handles this for you. In this version, what users call "databases" are called **data sources** in the API.
## Path A — `ntn` CLI (preferred, macOS / Linux)
### Raw API calls (shorthand for curl)
```bash
ntn api v1/users # GET
ntn api v1/pages parent[page_id]=abc123 \ # POST with inline body
properties[title][0][text][content]="Notes"
ntn api v1/pages/abc123 -X PATCH archived:=true # PATCH; := is non-string (bool/num/null)
```
Syntax notes:
- `key=value` — string fields
- `key[nested]=value` — nested object fields
- `key:=value` — typed assignment (booleans, numbers, null, arrays)
### Search
```bash
ntn api v1/search query="page title"
```
### Read page metadata
```bash
ntn api v1/pages/{page_id}
```
### Read page as Markdown (agent-friendly)
```bash
ntn api v1/pages/{page_id}/markdown
```
### Read page content as blocks
```bash
ntn api v1/blocks/{page_id}/children
```
### Create page from Markdown
```bash
ntn api v1/pages \
parent[page_id]=xxx \
properties[title][0][text][content]="Notes from meeting" \
markdown="# Agenda
- Q3 roadmap
- Hiring"
```
### Patch a page with Markdown
```bash
ntn api v1/pages/{page_id}/markdown -X PATCH \
markdown="## Update
Shipped the prototype."
```
### Query a database (data source)
```bash
ntn api v1/data_sources/{data_source_id}/query -X POST \
filter[property]=Status filter[select][equals]=Active
```
For complex queries with `sorts`, multiple filter clauses, or compound logic, pipe JSON in:
```bash
echo '{"filter": {"property": "Status", "select": {"equals": "Active"}}, "sorts": [{"property": "Date", "direction": "descending"}]}' | \
ntn api v1/data_sources/{data_source_id}/query -X POST --json -
```
### File uploads (one-liner — biggest CLI win)
```bash
ntn files create < photo.png
ntn files create --external-url https://example.com/photo.png
ntn files list
```
Compare to the 3-step HTTP flow (create upload → PUT bytes → reference).
### Useful env vars
| Var | Effect |
|---|---|
| `NOTION_API_TOKEN` | Auth token (overrides keychain) — set this to your integration token |
| `NOTION_KEYRING=0` | File-based creds at `~/.config/notion/auth.json` instead of OS keychain |
| `NOTION_WORKSPACE_ID` | Skip the workspace picker prompt |
## Path B — HTTP + curl (cross-platform, default on Windows)
All requests share this pattern:
```bash ```bash
curl -s -X GET "https://api.notion.com/v1/..." \ curl -s -X GET "https://api.notion.com/v1/..." \
@ -38,12 +161,9 @@ curl -s -X GET "https://api.notion.com/v1/..." \
-H "Content-Type: application/json" -H "Content-Type: application/json"
``` ```
The `Notion-Version` header is required. This skill uses `2025-09-03` (latest). In this version, databases are called "data sources" in the API. On Windows the `curl` shipped with Windows 10+ works as-is. PowerShell users can also use `Invoke-RestMethod`.
## Common Operations
### Search ### Search
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/search" \ curl -s -X POST "https://api.notion.com/v1/search" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -52,24 +172,56 @@ curl -s -X POST "https://api.notion.com/v1/search" \
-d '{"query": "page title"}' -d '{"query": "page title"}'
``` ```
### Get Page ### Read page metadata
```bash ```bash
curl -s "https://api.notion.com/v1/pages/{page_id}" \ curl -s "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" -H "Notion-Version: 2025-09-03"
``` ```
### Get Page Content (blocks) ### Read page as Markdown (agent-friendly)
Easier to feed to a model than block JSON.
```bash
curl -s "https://api.notion.com/v1/pages/{page_id}/markdown" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03"
```
### Read page content as blocks (when you need structure)
```bash ```bash
curl -s "https://api.notion.com/v1/blocks/{page_id}/children" \ curl -s "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" -H "Notion-Version: 2025-09-03"
``` ```
### Create Page in a Database ### Create page from Markdown
`POST /v1/pages` accepts a `markdown` body param.
```bash
curl -s -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"page_id": "xxx"},
"properties": {"title": [{"text": {"content": "Notes from meeting"}}]},
"markdown": "# Agenda\n\n- Q3 roadmap\n- Hiring\n\n## Decisions\n- Ship MVP Friday"
}'
```
### Patch a page with Markdown
```bash
curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}/markdown" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"markdown": "## Update\n\nShipped the prototype."}'
```
### Create page in a database (typed properties)
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/pages" \ curl -s -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -84,8 +236,7 @@ curl -s -X POST "https://api.notion.com/v1/pages" \
}' }'
``` ```
### Query a Database ### Query a database (data source)
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \ curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -97,8 +248,7 @@ curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query"
}' }'
``` ```
### Create a Database ### Create a database
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/data_sources" \ curl -s -X POST "https://api.notion.com/v1/data_sources" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -115,8 +265,7 @@ curl -s -X POST "https://api.notion.com/v1/data_sources" \
}' }'
``` ```
### Update Page Properties ### Update page properties
```bash ```bash
curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \ curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -125,8 +274,7 @@ curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
-d '{"properties": {"Status": {"select": {"name": "Done"}}}}' -d '{"properties": {"Status": {"select": {"name": "Done"}}}}'
``` ```
### Add Content to a Page ### Append blocks to a page
```bash ```bash
curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \ curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -139,6 +287,21 @@ curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
}' }'
``` ```
### File uploads (3-step flow)
```bash
# 1. Create upload
curl -s -X POST "https://api.notion.com/v1/file_uploads" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"filename": "photo.png", "content_type": "image/png"}'
# 2. PUT bytes to the upload_url returned above
curl -s -X PUT "{upload_url}" --data-binary @photo.png
# 3. Reference {file_upload_id} in a page/block payload
```
## Property Types ## Property Types
Common property formats for database items: Common property formats for database items:
@ -154,19 +317,132 @@ Common property formats for database items:
- **Email:** `{"email": "user@example.com"}` - **Email:** `{"email": "user@example.com"}`
- **Relation:** `{"relation": [{"id": "page_id"}]}` - **Relation:** `{"relation": [{"id": "page_id"}]}`
## Key Differences in API Version 2025-09-03 ## API Version 2025-09-03 — Databases vs Data Sources
- **Databases → Data Sources:** Use `/data_sources/` endpoints for queries and retrieval - **Databases became data sources.** Use `/data_sources/` endpoints for queries and retrieval.
- **Two IDs:** Each database has both a `database_id` and a `data_source_id` - **Two IDs per database:** `database_id` and `data_source_id`.
- Use `database_id` when creating pages (`parent: {"database_id": "..."}`) - `database_id` when creating pages: `parent: {"database_id": "..."}`
- Use `data_source_id` when querying (`POST /v1/data_sources/{id}/query`) - `data_source_id` when querying: `POST /v1/data_sources/{id}/query`
- **Search results:** Databases return as `"object": "data_source"` with their `data_source_id` - Search returns databases as `"object": "data_source"` with the `data_source_id` field.
## Notion Workers (advanced, requires `ntn`)
Workers are TypeScript programs Notion hosts for you. One worker can expose any combination of:
- **Syncs** — pull data from external APIs into a Notion database on a schedule (default 30 min).
- **Tools** — appear as callable tools inside Notion's Custom Agents.
- **Webhooks** — receive HTTP events from external services (GitHub, Stripe, etc.) and act in Notion.
**Plan / platform gating:**
- CLI works on all plans. **Deploying Workers requires Business or Enterprise.**
- `ntn` is macOS/Linux only as of May 2026. Windows users need WSL2 or to wait for native support.
- Free through August 11, 2026; metered on Notion credits after.
### Minimal Worker
```bash
ntn workers new my-worker # scaffold
cd my-worker
# Edit src/index.ts
ntn workers deploy --name my-worker
```
`src/index.ts`:
```typescript
import { Worker } from "@notionhq/workers";
const worker = new Worker();
export default worker;
worker.tool("greet", {
title: "Greet a User",
description: "Returns a friendly greeting",
inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] },
execute: async ({ name }) => `Hello, ${name}!`,
});
```
### Webhook capability
```typescript
worker.webhook("onGithubPush", {
title: "GitHub Push Handler",
execute: async (events, { notion }) => {
for (const event of events) {
// event.body, event.rawBody (for signature verification), event.headers
console.log("got delivery", event.deliveryId);
}
},
});
```
After deploy: `ntn workers webhooks list` shows the URL Notion generates. Treat that URL as a secret — anyone with it can POST events unless you add signature verification.
### Worker lifecycle commands
```bash
ntn workers deploy
ntn workers list
ntn workers exec <capability-key> -d '{"name": "world"}'
ntn workers sync trigger <key> # run a sync now
ntn workers sync pause <key>
ntn workers env set GITHUB_WEBHOOK_SECRET=...
ntn workers runs list # recent invocations
ntn workers runs logs <run-id>
ntn workers webhooks list
```
When asked to build a Worker, scaffold with `ntn workers new`, write the code in `src/index.ts`, set any secrets with `ntn workers env set`, and deploy. Notion's docs at https://developers.notion.com/workers cover the full API surface.
## Notion-Flavored Markdown (used by `/markdown` endpoints)
Standard CommonMark plus XML-like tags for Notion-specific blocks. Use **tabs** for indentation.
**Blocks beyond CommonMark:**
```
<callout icon="🎯" color="blue_bg">
Ship the MVP by **Friday**.
</callout>
<details color="gray">
<summary>Toggle title</summary>
Children indented one tab
</details>
<columns>
<column>Left side</column>
<column>Right side</column>
</columns>
<table_of_contents color="gray"/>
```
**Inline:**
- Mentions: `<mention-user url="..."/>`, `<mention-page url="...">Title</mention-page>`, `<mention-date start="2026-05-15"/>`
- Underline: `<span underline="true">text</span>`
- Color: `<span color="blue">text</span>` or block-level `{color="blue"}` on the first line
- Math: inline `$x^2$`, block `$$ ... $$`
- Citations: `[^https://example.com]`
**Colors:** `gray brown orange yellow green blue purple pink red`, plus `*_bg` variants for backgrounds.
Headings 5/6 collapse to H4. Multiple `>` lines render as separate quote blocks — use `<br>` inside a single `>` for multi-line quotes.
## Choosing the Right Path
| Task | mac / Linux | Windows |
|---|---|---|
| Read/write pages, search, query databases | `ntn api ...` | curl |
| Read a page for an agent to summarize | `ntn api v1/pages/{id}/markdown` | curl `/markdown` endpoint |
| Upload a file | `ntn files create < file` | 3-step HTTP flow |
| One-off API exploration | `ntn api ...` | curl |
| Build a sync / webhook / agent tool hosted by Notion | `ntn workers ...` | WSL2 + `ntn workers ...` |
## Notes ## Notes
- Page/database IDs are UUIDs (with or without dashes) - Page/database IDs are UUIDs (with or without dashes — both accepted).
- Rate limit: ~3 requests/second average - Rate limit: ~3 requests/second average. The CLI doesn't bypass this.
- The API cannot set database view filters — that's UI-only - The API cannot set database **view** filters — that's UI-only.
- Use `is_inline: true` when creating data sources to embed them in pages - Use `"is_inline": true` when creating data sources to embed them in a page.
- Add `-s` flag to curl to suppress progress bars (cleaner output for Hermes) - Always pass `-s` to curl to suppress progress bars (cleaner agent output).
- Pipe output through `jq` for readable JSON: `... | jq '.results[0].properties'` - Pipe JSON through `jq` when reading: `... | jq '.results[0].properties'`.
- Notion also ships an MCP server now (`Notion MCP`, ~91% more token-efficient on DB ops than the previous version) — wire it via Hermes' MCP support if you want streaming Notion access from inside a session, but the paths above are enough for most one-shot tasks.

View file

@ -144,7 +144,7 @@ If a skill is missing from this list but present in the repo, the catalog is reg
| [`linear`](/docs/user-guide/skills/bundled/productivity/productivity-linear) | Linear: manage issues, projects, teams via GraphQL + curl. | `productivity/linear` | | [`linear`](/docs/user-guide/skills/bundled/productivity/productivity-linear) | Linear: manage issues, projects, teams via GraphQL + curl. | `productivity/linear` |
| [`maps`](/docs/user-guide/skills/bundled/productivity/productivity-maps) | Geocode, POIs, routes, timezones via OpenStreetMap/OSRM. | `productivity/maps` | | [`maps`](/docs/user-guide/skills/bundled/productivity/productivity-maps) | Geocode, POIs, routes, timezones via OpenStreetMap/OSRM. | `productivity/maps` |
| [`nano-pdf`](/docs/user-guide/skills/bundled/productivity/productivity-nano-pdf) | Edit PDF text/typos/titles via nano-pdf CLI (NL prompts). | `productivity/nano-pdf` | | [`nano-pdf`](/docs/user-guide/skills/bundled/productivity/productivity-nano-pdf) | Edit PDF text/typos/titles via nano-pdf CLI (NL prompts). | `productivity/nano-pdf` |
| [`notion`](/docs/user-guide/skills/bundled/productivity/productivity-notion) | Notion API via curl: pages, databases, blocks, search. | `productivity/notion` | | [`notion`](/docs/user-guide/skills/bundled/productivity/productivity-notion) | Notion API + ntn CLI: pages, databases, markdown, Workers. | `productivity/notion` |
| [`ocr-and-documents`](/docs/user-guide/skills/bundled/productivity/productivity-ocr-and-documents) | Extract text from PDFs/scans (pymupdf, marker-pdf). | `productivity/ocr-and-documents` | | [`ocr-and-documents`](/docs/user-guide/skills/bundled/productivity/productivity-ocr-and-documents) | Extract text from PDFs/scans (pymupdf, marker-pdf). | `productivity/ocr-and-documents` |
| [`powerpoint`](/docs/user-guide/skills/bundled/productivity/productivity-powerpoint) | Create, read, edit .pptx decks, slides, notes, templates. | `productivity/powerpoint` | | [`powerpoint`](/docs/user-guide/skills/bundled/productivity/productivity-powerpoint) | Create, read, edit .pptx decks, slides, notes, templates. | `productivity/powerpoint` |
| [`teams-meeting-pipeline`](/docs/user-guide/skills/bundled/productivity/productivity-teams-meeting-pipeline) | Operate the Teams meeting summary pipeline via Hermes CLI — summarize meetings, inspect pipeline status, replay jobs, manage Microsoft Graph subscriptions. | `productivity/teams-meeting-pipeline` | | [`teams-meeting-pipeline`](/docs/user-guide/skills/bundled/productivity/productivity-teams-meeting-pipeline) | Operate the Teams meeting summary pipeline via Hermes CLI — summarize meetings, inspect pipeline status, replay jobs, manage Microsoft Graph subscriptions. | `productivity/teams-meeting-pipeline` |

View file

@ -1,14 +1,14 @@
--- ---
title: "Notion — Notion API via curl: pages, databases, blocks, search" title: "Notion — Notion API + ntn CLI: pages, databases, markdown, Workers"
sidebar_label: "Notion" sidebar_label: "Notion"
description: "Notion API via curl: pages, databases, blocks, search" description: "Notion API + ntn CLI: pages, databases, markdown, Workers"
--- ---
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */} {/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
# Notion # Notion
Notion API via curl: pages, databases, blocks, search. Notion API + ntn CLI: pages, databases, markdown, Workers.
## Skill metadata ## Skill metadata
@ -16,11 +16,11 @@ Notion API via curl: pages, databases, blocks, search.
|---|---| |---|---|
| Source | Bundled (installed by default) | | Source | Bundled (installed by default) |
| Path | `skills/productivity/notion` | | Path | `skills/productivity/notion` |
| Version | `1.0.0` | | Version | `2.0.0` |
| Author | community | | Author | community |
| License | MIT | | License | MIT |
| Platforms | linux, macos, windows | | Platforms | linux, macos, windows |
| Tags | `Notion`, `Productivity`, `Notes`, `Database`, `API` | | Tags | `Notion`, `Productivity`, `Notes`, `Database`, `API`, `CLI`, `Workers` |
## Reference: full SKILL.md ## Reference: full SKILL.md
@ -28,23 +28,146 @@ Notion API via curl: pages, databases, blocks, search.
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active. The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
::: :::
# Notion API # Notion
Use the Notion API via curl to create, read, update pages, databases (data sources), and blocks. No extra tools needed — just curl and a Notion API key. Talk to Notion two ways. Same integration token works for both — pick by what's available.
## Prerequisites **`ntn` CLI** — Notion's official CLI. Shorter syntax, one-line file uploads, required for Workers. macOS + Linux only as of May 2026 (Windows support "coming soon"). **Default when installed.**
**HTTP + curl** — works everywhere including Windows. **Default fallback** when `ntn` isn't installed.
## Setup
### 1. Get an integration token (required for both paths)
1. Create an integration at https://notion.so/my-integrations 1. Create an integration at https://notion.so/my-integrations
2. Copy the API key (starts with `ntn_` or `secret_`) 2. Copy the API key (starts with `ntn_` or `secret_`)
3. Store it in `~/.hermes/.env`: 3. Store in `~/.hermes/.env`:
``` ```
NOTION_API_KEY=ntn_your_key_here NOTION_API_KEY=ntn_your_key_here
``` ```
4. **Important:** Share target pages/databases with your integration in Notion (click "..." → "Connect to" → your integration name) 4. **Share target pages/databases with the integration** in Notion: page menu `...``Connect to` → your integration name. Without this, the API returns 404 for that page even though it exists.
### 2. Install `ntn` (preferred path on macOS / Linux)
```bash
# Recommended
curl -fsSL https://ntn.dev | bash
# Or via npm (needs Node 22+, npm 10+)
npm install --global ntn
ntn --version # verify
```
**Skip `ntn login` — use the integration token instead.** This works headlessly, no browser needed:
```bash
export NOTION_API_TOKEN=$NOTION_API_KEY # ntn reads NOTION_API_TOKEN
export NOTION_KEYRING=0 # don't try to use the OS keychain
```
Add those exports to your shell profile (or to `~/.hermes/.env`) so every session inherits them.
### 3. Choose path at runtime
```bash
if command -v ntn >/dev/null 2>&1; then
# use ntn
else
# fall back to curl
fi
```
Windows users: skip step 2 entirely until native `ntn` ships — Path B works fine. If you want CLI ergonomics now, install `ntn` inside WSL2.
## API Basics ## API Basics
All requests use this pattern: `Notion-Version: 2025-09-03` is required on all HTTP requests. `ntn` handles this for you. In this version, what users call "databases" are called **data sources** in the API.
## Path A — `ntn` CLI (preferred, macOS / Linux)
### Raw API calls (shorthand for curl)
```bash
ntn api v1/users # GET
ntn api v1/pages parent[page_id]=abc123 \ # POST with inline body
properties[title][0][text][content]="Notes"
ntn api v1/pages/abc123 -X PATCH archived:=true # PATCH; := is non-string (bool/num/null)
```
Syntax notes:
- `key=value` — string fields
- `key[nested]=value` — nested object fields
- `key:=value` — typed assignment (booleans, numbers, null, arrays)
### Search
```bash
ntn api v1/search query="page title"
```
### Read page metadata
```bash
ntn api v1/pages/{page_id}
```
### Read page as Markdown (agent-friendly)
```bash
ntn api v1/pages/{page_id}/markdown
```
### Read page content as blocks
```bash
ntn api v1/blocks/{page_id}/children
```
### Create page from Markdown
```bash
ntn api v1/pages \
parent[page_id]=xxx \
properties[title][0][text][content]="Notes from meeting" \
markdown="# Agenda
- Q3 roadmap
- Hiring"
```
### Patch a page with Markdown
```bash
ntn api v1/pages/{page_id}/markdown -X PATCH \
markdown="## Update
Shipped the prototype."
```
### Query a database (data source)
```bash
ntn api v1/data_sources/{data_source_id}/query -X POST \
filter[property]=Status filter[select][equals]=Active
```
For complex queries with `sorts`, multiple filter clauses, or compound logic, pipe JSON in:
```bash
echo '{"filter": {"property": "Status", "select": {"equals": "Active"}}, "sorts": [{"property": "Date", "direction": "descending"}]}' | \
ntn api v1/data_sources/{data_source_id}/query -X POST --json -
```
### File uploads (one-liner — biggest CLI win)
```bash
ntn files create < photo.png
ntn files create --external-url https://example.com/photo.png
ntn files list
```
Compare to the 3-step HTTP flow (create upload → PUT bytes → reference).
### Useful env vars
| Var | Effect |
|---|---|
| `NOTION_API_TOKEN` | Auth token (overrides keychain) — set this to your integration token |
| `NOTION_KEYRING=0` | File-based creds at `~/.config/notion/auth.json` instead of OS keychain |
| `NOTION_WORKSPACE_ID` | Skip the workspace picker prompt |
## Path B — HTTP + curl (cross-platform, default on Windows)
All requests share this pattern:
```bash ```bash
curl -s -X GET "https://api.notion.com/v1/..." \ curl -s -X GET "https://api.notion.com/v1/..." \
@ -53,12 +176,9 @@ curl -s -X GET "https://api.notion.com/v1/..." \
-H "Content-Type: application/json" -H "Content-Type: application/json"
``` ```
The `Notion-Version` header is required. This skill uses `2025-09-03` (latest). In this version, databases are called "data sources" in the API. On Windows the `curl` shipped with Windows 10+ works as-is. PowerShell users can also use `Invoke-RestMethod`.
## Common Operations
### Search ### Search
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/search" \ curl -s -X POST "https://api.notion.com/v1/search" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -67,24 +187,56 @@ curl -s -X POST "https://api.notion.com/v1/search" \
-d '{"query": "page title"}' -d '{"query": "page title"}'
``` ```
### Get Page ### Read page metadata
```bash ```bash
curl -s "https://api.notion.com/v1/pages/{page_id}" \ curl -s "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" -H "Notion-Version: 2025-09-03"
``` ```
### Get Page Content (blocks) ### Read page as Markdown (agent-friendly)
Easier to feed to a model than block JSON.
```bash
curl -s "https://api.notion.com/v1/pages/{page_id}/markdown" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03"
```
### Read page content as blocks (when you need structure)
```bash ```bash
curl -s "https://api.notion.com/v1/blocks/{page_id}/children" \ curl -s "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" -H "Notion-Version: 2025-09-03"
``` ```
### Create Page in a Database ### Create page from Markdown
`POST /v1/pages` accepts a `markdown` body param.
```bash
curl -s -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"page_id": "xxx"},
"properties": {"title": [{"text": {"content": "Notes from meeting"}}]},
"markdown": "# Agenda\n\n- Q3 roadmap\n- Hiring\n\n## Decisions\n- Ship MVP Friday"
}'
```
### Patch a page with Markdown
```bash
curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}/markdown" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"markdown": "## Update\n\nShipped the prototype."}'
```
### Create page in a database (typed properties)
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/pages" \ curl -s -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -99,8 +251,7 @@ curl -s -X POST "https://api.notion.com/v1/pages" \
}' }'
``` ```
### Query a Database ### Query a database (data source)
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \ curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -112,8 +263,7 @@ curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query"
}' }'
``` ```
### Create a Database ### Create a database
```bash ```bash
curl -s -X POST "https://api.notion.com/v1/data_sources" \ curl -s -X POST "https://api.notion.com/v1/data_sources" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -130,8 +280,7 @@ curl -s -X POST "https://api.notion.com/v1/data_sources" \
}' }'
``` ```
### Update Page Properties ### Update page properties
```bash ```bash
curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \ curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -140,8 +289,7 @@ curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
-d '{"properties": {"Status": {"select": {"name": "Done"}}}}' -d '{"properties": {"Status": {"select": {"name": "Done"}}}}'
``` ```
### Add Content to a Page ### Append blocks to a page
```bash ```bash
curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \ curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_API_KEY" \ -H "Authorization: Bearer $NOTION_API_KEY" \
@ -154,6 +302,21 @@ curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
}' }'
``` ```
### File uploads (3-step flow)
```bash
# 1. Create upload
curl -s -X POST "https://api.notion.com/v1/file_uploads" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"filename": "photo.png", "content_type": "image/png"}'
# 2. PUT bytes to the upload_url returned above
curl -s -X PUT "{upload_url}" --data-binary @photo.png
# 3. Reference {file_upload_id} in a page/block payload
```
## Property Types ## Property Types
Common property formats for database items: Common property formats for database items:
@ -169,19 +332,132 @@ Common property formats for database items:
- **Email:** `{"email": "user@example.com"}` - **Email:** `{"email": "user@example.com"}`
- **Relation:** `{"relation": [{"id": "page_id"}]}` - **Relation:** `{"relation": [{"id": "page_id"}]}`
## Key Differences in API Version 2025-09-03 ## API Version 2025-09-03 — Databases vs Data Sources
- **Databases → Data Sources:** Use `/data_sources/` endpoints for queries and retrieval - **Databases became data sources.** Use `/data_sources/` endpoints for queries and retrieval.
- **Two IDs:** Each database has both a `database_id` and a `data_source_id` - **Two IDs per database:** `database_id` and `data_source_id`.
- Use `database_id` when creating pages (`parent: {"database_id": "..."}`) - `database_id` when creating pages: `parent: {"database_id": "..."}`
- Use `data_source_id` when querying (`POST /v1/data_sources/{id}/query`) - `data_source_id` when querying: `POST /v1/data_sources/{id}/query`
- **Search results:** Databases return as `"object": "data_source"` with their `data_source_id` - Search returns databases as `"object": "data_source"` with the `data_source_id` field.
## Notion Workers (advanced, requires `ntn`)
Workers are TypeScript programs Notion hosts for you. One worker can expose any combination of:
- **Syncs** — pull data from external APIs into a Notion database on a schedule (default 30 min).
- **Tools** — appear as callable tools inside Notion's Custom Agents.
- **Webhooks** — receive HTTP events from external services (GitHub, Stripe, etc.) and act in Notion.
**Plan / platform gating:**
- CLI works on all plans. **Deploying Workers requires Business or Enterprise.**
- `ntn` is macOS/Linux only as of May 2026. Windows users need WSL2 or to wait for native support.
- Free through August 11, 2026; metered on Notion credits after.
### Minimal Worker
```bash
ntn workers new my-worker # scaffold
cd my-worker
# Edit src/index.ts
ntn workers deploy --name my-worker
```
`src/index.ts`:
```typescript
import { Worker } from "@notionhq/workers";
const worker = new Worker();
export default worker;
worker.tool("greet", {
title: "Greet a User",
description: "Returns a friendly greeting",
inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] },
execute: async ({ name }) => `Hello, ${name}!`,
});
```
### Webhook capability
```typescript
worker.webhook("onGithubPush", {
title: "GitHub Push Handler",
execute: async (events, { notion }) => {
for (const event of events) {
// event.body, event.rawBody (for signature verification), event.headers
console.log("got delivery", event.deliveryId);
}
},
});
```
After deploy: `ntn workers webhooks list` shows the URL Notion generates. Treat that URL as a secret — anyone with it can POST events unless you add signature verification.
### Worker lifecycle commands
```bash
ntn workers deploy
ntn workers list
ntn workers exec <capability-key> -d '{"name": "world"}'
ntn workers sync trigger <key> # run a sync now
ntn workers sync pause <key>
ntn workers env set GITHUB_WEBHOOK_SECRET=...
ntn workers runs list # recent invocations
ntn workers runs logs <run-id>
ntn workers webhooks list
```
When asked to build a Worker, scaffold with `ntn workers new`, write the code in `src/index.ts`, set any secrets with `ntn workers env set`, and deploy. Notion's docs at https://developers.notion.com/workers cover the full API surface.
## Notion-Flavored Markdown (used by `/markdown` endpoints)
Standard CommonMark plus XML-like tags for Notion-specific blocks. Use **tabs** for indentation.
**Blocks beyond CommonMark:**
```
<callout icon="🎯" color="blue_bg">
Ship the MVP by **Friday**.
</callout>
<details color="gray">
<summary>Toggle title</summary>
Children indented one tab
</details>
<columns>
<column>Left side</column>
<column>Right side</column>
</columns>
<table_of_contents color="gray"/>
```
**Inline:**
- Mentions: `<mention-user url="..."/>`, `<mention-page url="...">Title</mention-page>`, `<mention-date start="2026-05-15"/>`
- Underline: `<span underline="true">text</span>`
- Color: `<span color="blue">text</span>` or block-level `{color="blue"}` on the first line
- Math: inline `$x^2$`, block `$$ ... $$`
- Citations: `[^https://example.com]`
**Colors:** `gray brown orange yellow green blue purple pink red`, plus `*_bg` variants for backgrounds.
Headings 5/6 collapse to H4. Multiple `>` lines render as separate quote blocks — use `<br>` inside a single `>` for multi-line quotes.
## Choosing the Right Path
| Task | mac / Linux | Windows |
|---|---|---|
| Read/write pages, search, query databases | `ntn api ...` | curl |
| Read a page for an agent to summarize | `ntn api v1/pages/{id}/markdown` | curl `/markdown` endpoint |
| Upload a file | `ntn files create < file` | 3-step HTTP flow |
| One-off API exploration | `ntn api ...` | curl |
| Build a sync / webhook / agent tool hosted by Notion | `ntn workers ...` | WSL2 + `ntn workers ...` |
## Notes ## Notes
- Page/database IDs are UUIDs (with or without dashes) - Page/database IDs are UUIDs (with or without dashes — both accepted).
- Rate limit: ~3 requests/second average - Rate limit: ~3 requests/second average. The CLI doesn't bypass this.
- The API cannot set database view filters — that's UI-only - The API cannot set database **view** filters — that's UI-only.
- Use `is_inline: true` when creating data sources to embed them in pages - Use `"is_inline": true` when creating data sources to embed them in a page.
- Add `-s` flag to curl to suppress progress bars (cleaner output for Hermes) - Always pass `-s` to curl to suppress progress bars (cleaner agent output).
- Pipe output through `jq` for readable JSON: `... | jq '.results[0].properties'` - Pipe JSON through `jq` when reading: `... | jq '.results[0].properties'`.
- Notion also ships an MCP server now (`Notion MCP`, ~91% more token-efficient on DB ops than the previous version) — wire it via Hermes' MCP support if you want streaming Notion access from inside a session, but the paths above are enough for most one-shot tasks.