Use it when your goal in databases and data is clear and you can limit it to the data and actions it actually needs.
Notion
Notion API + ntn CLI: pages, databases, markdown, Workers
What does it add to Hermes?
Notion API + ntn CLI: pages, databases, markdown, Workers
Notion is a skill related to databases and data. It gives the agent a structured way to read or query data. Some tools may also write data, so clear limits matter.
This plain-language explanation is based on the publisher description. The original text remains visible for verification.
Do not add it merely to experiment when Hermes already has a simpler path, or when you cannot review its source and permissions.
Best for users who want a repeatable way of working inside Hermes.
Use a test database and a read-only account, then request a simple query that changes no records.
Notion API + ntn CLI: pages, databases, markdown, Workers
This entry was indexed from Hermes Bundled Skills. Our explanation interprets the type and domain without inventing a capability not present upstream.
The source is official or editorially reviewed, but you still need to review permissions and version compatibility.
Inspect, install, then test.
- 01Open the source
Match the publisher, license, and description to your need. Check the real update history.
- 02Review permissions and secrets
Never paste a secret value into this site. Use environment-variable names and grant the smallest scope.
- 03Copy setup only after review
The controls below copy text. They do not execute commands on your device.
- 04Test with a non-sensitive task
Inspect the visible tools, then exclude write or delete tools you do not need.
Already installed with Hermes.
This skill ships with Hermes and loads when the agent decides it is relevant. There is nothing to install; read the definition below so you know what it will do.
Open the official page ↗Exactly what Hermes loads when this skill runs.
Reproduced from the official documentation. Read it before enabling the skill: this text becomes the agent's instructions.
Notion API + ntn CLI: pages, databases, markdown, Workers.
Skill metadata
A lookup table. Do not read it all; find the row that applies to you.
| Source | Bundled (installed by default) |
| Path | skills/productivity/notion |
| Version | 2.0.0 |
| Author | community |
| License | MIT |
| Platforms | linux, macos, windows |
| Tags | Notion, Productivity, Notes, Database, API, CLI, Workers |
Reference: full SKILL.md
Explains the idea itself. Read it slowly; the later sections build on it.
Talk to Notion two ways. Same integration token works for both — pick by what's available.
◆ 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
Ordered, practical steps. Run one and confirm it worked before moving on.
1. Get an integration token (required for both paths)
- Create an integration at https://notion.so/my-integrations
- Copy the API key (starts with
ntn_orsecret_) - Store in
${HERMES_HOME:-~/.hermes}/.env:
NOTION_API_KEY=ntn_your_key_here- 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)
# Recommended
curl -fsSL https://ntn.dev | bash
# Or via npm (needs Node 22+, npm 10+)
npm install --global ntn
ntn --version # verifySkip ntn login — use the integration token instead. This works headlessly, no browser needed:
export NOTION_API_TOKEN=$NOTION_API_KEY # ntn reads NOTION_API_TOKEN
export NOTION_KEYRING=0 # don't try to use the OS keychainAdd those exports to your shell profile (or to ${HERMES_HOME:-~/.hermes}/.env) so every session inherits them.
3. Choose path at runtime
if command -v ntn >/dev/null 2>&1; then
# use ntn
else
# fall back to curl
fiWindows 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
Explains the idea itself. Read it slowly; the later sections build on it.
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)
Explains the idea itself. Read it slowly; the later sections build on it.
Raw API calls (shorthand for curl)
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 fieldskey[nested]=value— nested object fieldskey:=value— typed assignment (booleans, numbers, null, arrays)
Search
ntn api v1/search query="page title"Read page metadata
ntn api v1/pages/{page_id}Read page as Markdown (agent-friendly)
ntn api v1/pages/{page_id}/markdownRead page content as blocks
ntn api v1/blocks/{page_id}/childrenCreate page from Markdown
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
ntn api v1/pages/{page_id}/markdown -X PATCH \
markdown="## Update
Shipped the prototype."Query a database (data source)
ntn api v1/data_sources/{data_source_id}/query -X POST \
filter[property]=Status filter[select][equals]=ActiveFor complex queries with sorts, multiple filter clauses, or compound logic, pipe JSON in:
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)
ntn files create < photo.png
ntn files create --external-url https://example.com/photo.png
ntn files listCompare 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)
Settings you configure once. Change one at a time so you can see what each does. Set NOTION_API_KEY in your environment, not in the chat.
All requests share this pattern:
curl -s -X GET "https://api.notion.com/v1/..." \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json"On Windows the curl shipped with Windows 10+ works as-is. PowerShell users can also use Invoke-RestMethod.
Search
curl -s -X POST "https://api.notion.com/v1/search" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"query": "page title"}'Read page metadata
curl -s "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03"Read page as Markdown (agent-friendly)
Easier to feed to a model than block JSON.
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)
curl -s "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03"Create page from Markdown
POST /v1/pages accepts a markdown body param.
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
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)
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": {"database_id": "xxx"},
"properties": {
"Name": {"title": [{"text": {"content": "New Item"}}]},
"Status": {"select": {"name": "Todo"}}
}
}'Query a database (data source)
curl -s -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"filter": {"property": "Status", "select": {"equals": "Active"}},
"sorts": [{"property": "Date", "direction": "descending"}]
}'Create a database
curl -s -X POST "https://api.notion.com/v1/data_sources" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"page_id": "xxx"},
"title": [{"text": {"content": "My Database"}}],
"properties": {
"Name": {"title": {}},
"Status": {"select": {"options": [{"name": "Todo"}, {"name": "Done"}]}},
"Date": {"date": {}}
}
}'Update page properties
curl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"properties": {"Status": {"select": {"name": "Done"}}}}'Append blocks to a page
curl -s -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"children": [
{"object": "block", "type": "paragraph", "paragraph": {"rich_text": [{"text": {"content": "Hello from Hermes!"}}]}}
]
}'File uploads (3-step flow)
# 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 payloadProperty Types
Explains the idea itself. Read it slowly; the later sections build on it.
Common property formats for database items:
- Title:
{"title": [{"text": {"content": "..."}}]} - Rich text:
{"rich_text": [{"text": {"content": "..."}}]} - Select:
{"select": {"name": "Option"}} - Multi-select:
{"multi_select": [{"name": "A"}, {"name": "B"}]} - Date:
{"date": {"start": "2026-01-15", "end": "2026-01-16"}} - Checkbox:
{"checkbox": true} - Number:
{"number": 42} - URL:
{"url": "https://..."} - Email:
{"email": "user@example.com"} - Relation:
{"relation": [{"id": "page_id"}]}
API Version 2025-09-03 — Databases vs Data Sources
Explains the idea itself. Read it slowly; the later sections build on it.
- Databases became data sources. Use
/data_sources/endpoints for queries and retrieval. - Two IDs per database:
database_idanddata_source_id. database_idwhen creating pages:parent: {"database_id": "..."}data_source_idwhen querying:POST /v1/data_sources/{id}/query- Search returns databases as
"object": "data_source"with thedata_source_idfield.
Notion Workers (advanced, requires `ntn`)
Settings you configure once. Change one at a time so you can see what each does. Set GITHUB_WEBHOOK_SECRET in your environment, not in the chat.
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.
ntnis 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
ntn workers new my-worker # scaffold
cd my-worker
# Edit src/index.ts
ntn workers deploy --name my-workersrc/index.ts:
const worker = new 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
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
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 listWhen 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)
Explains the idea itself. Read it slowly; the later sections build on it.
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
A lookup table. Do not read it all; find the row that applies to you.
| 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
Explains the idea itself. Read it slowly; the later sections build on it.
- Page/database IDs are UUIDs (with or without dashes — both accepted).
- Rate limit: ~3 requests/second average. The CLI doesn't bypass this.
- The API cannot set database view filters — that's UI-only.
- Use
"is_inline": truewhen creating data sources to embed them in a page. - Always pass
-sto curl to suppress progress bars (cleaner agent output). - Pipe JSON through
jqwhen 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.