# Notion

> Notion API + ntn CLI: pages, databases, markdown, Workers.

- **Type:** Skill
- **Install:** `agentstack add skill-atlasomnia-donna-starter-notion`
- **Verified:** Pending review
- **Seller:** [AtlasOmnia](https://agentstack.voostack.com/s/atlasomnia)
- **Installs:** 0
- **Category:** [Productivity](https://agentstack.voostack.com/c/productivity)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [AtlasOmnia](https://github.com/AtlasOmnia)
- **Source:** https://github.com/AtlasOmnia/donna-starter/tree/main/skills/productivity/notion

## Install

```sh
agentstack add skill-atlasomnia-donna-starter-notion
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Notion

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

### 1. Get an integration token (required for both paths)

1. Create an integration at https://notion.so/my-integrations
2. Copy the API key (starts with `ntn_` or `secret_`)
3. Store in `${HERMES_HOME:-~/.hermes}/.env`:
 ```
 NOTION_API_KEY=ntn_yo...re
 ```
4. **Ensure the integration is connected to the workspace** — before sharing any pages, verify the integration appears in your workspace's connection list:
 - Open any page in Notion → click **Share** (top-right) → **⚙️ Settings** → **Connections** tab
 - The integration should appear under **Connected apps**. If NOT listed, the API key is valid but the integration has no workspace linkage — every API call will return `404 object_not_found` regardless of page sharing.
 - To connect: go to **https://notion.so/my-integrations** → click your integration → in the **Connected pages** section, enable **"Full workspace access"** (or connect individual top-level pages manually).
 - After connecting, verify it shows up in the Connections panel before proceeding.

5. **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.

### 1b. Grant workspace-wide access (optional, recommended for single-user workspaces)

To avoid connecting pages one by one, grant the integration full workspace access:
1. Open **https://notion.so/my-integrations**
2. Find your integration → click it
3. Change access from *Selected pages* to **Full workspace access** (exact label may vary — look for a toggle or dropdown)
4. This makes all existing and future pages visible to the integration by default

**Tip:** For single-user workspaces, Option 1b is the cleanest approach. For shared workspaces, connect individual top-level pages instead — child pages inherit access from their parent.

5. **For full workspace access (single-user workspaces):** Open https://notion.so/my-integrations → click your integration → enable **"Full workspace access"** in the Connected pages section. This avoids having to manually connect every page. Child pages inherit access from their parent, so connecting top-level pages is sufficient if you prefer granular control.

### 2. Install `ntn` (preferred path on macOS / Linux)

```bash
### 2. Install `ntn` (preferred path on macOS / Linux)

```bash
# Recommended — use custom install dir if /usr/local/bin needs sudo
curl -fsSL https://ntn.dev | bash

# Or to a user-writable location (fallback when /usr/local/bin is locked):
curl -fsSL "https://ntn.dev" | NTN_INSTALL_DIR="$HOME/.local/bin" bash

# Or via npm (needs Node 22+, npm 10+)
npm install --global ntn

ntn --version # verify
```

**macOS note:** On recent macOS, `/usr/local/bin` may require `sudo`. Use `NTN_INSTALL_DIR="$HOME/.local/bin"` as a fallback — just ensure `$HOME/.local/bin` is on your `PATH`.

**Skip `ntn login` — use the integration token instead.** This works headlessly, no browser needed:
```bash
export NOTION_API_TOKEN=*** # ntn reads NOTION_API_TOKEN (not NOTION_API_KEY)
export NOTION_KEYRING=0 # don't try to use the OS keychain
```

Add those exports to your shell profile (or to `${HERMES_HOME:-~/.hermes}/.env`) so every session inherits them.

> **Pitfall:** On macOS, `curl -fsSL https://ntn.dev | bash` fails with "Could not install to /usr/local/bin" — use `NTN_INSTALL_DIR="$HOME/.local/bin"` instead. Ensure `$HOME/.local/bin` is on your PATH.

### 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

`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
The current endpoint uses a command-style discriminated union. For a full replacement:
```bash
ntn api v1/pages/{page_id}/markdown -X PATCH \
 type=replace_content \
 replace_content[new_str]="## Update

Shipped the prototype."
```
For pages containing child pages or databases, prefer `insert_content` or `update_content`; `replace_content` refuses to delete protected child content unless `allow_deleting_content=true`.

### 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  `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  -d '{"name": "world"}'
ntn workers sync trigger  # run a sync now
ntn workers sync pause 
ntn workers env set GITHUB_WEBHOOK_SECRET=...
ntn workers runs list # recent invocations
ntn workers runs logs 
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:**
```

	Ship the MVP by **Friday**.

Toggle title
	Children indented one tab

	Left side
	Right side

```

**Inline:**
- Mentions: ``, `Title`, ``
- Underline: `text`
- Color: `text` 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 `` 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 /`) and proceed with API calls. The CUA path is useful for diagnosing *why* the integration isn't visible — it reveals whether the issue is page sharing vs. workspace connection.
- **Bot integrations cannot create workspace-root pages OR databases:** The API v2025-09-03 requires `parent.page_id` or `parent.database_id` for all page AND database creation. Bot integrations (internal, not public) have no `member_page`. To create top-level content, you MUST first have a manually-created parent page shared with the integration — then nest everything under it. The workspace-parent shortcut (`"parent": {"type": "workspace"}`) works for neither pages nor databases on bot integrations — both return `validation_error` asking for `parent.page_id`.
- **ntn CLI headless auth requires TWO env vars:** `NOTION_API_TOKEN` (the integration key) AND `NOTION_WORKSPACE_ID` (from `/v1/users/me` → `bot.workspace_id`). Without `NOTION_WORKSPACE_ID`, ntn errors with "No workspace selected." Set both:
 ```bash
 export NOTION_API_TOKEN=***
 export NOTION_WORKSPACE_ID=
 ```
- **Archived data_sources are stuck:** Once a database (data_source) is archived via `PATCH /v1/data_sources/{id}`, it cannot be unarchived through any endpoint — `/v1/pages/`, `/v1/databases/`, and `/v1/data_sources/` all return 400 or 404. Recreate instead of trying to restore.
- **Bulk archiving hits type mismatches:** When searching for items to archive, database rows appear in search results but their IDs may not resolve on `/v1/pages/` (404) nor on `/v1/data_sources/` (wrong type). The reliable path: collect unique parent database IDs from search results and archive the parent databases — their rows disappear with them.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [AtlasOmnia](https://github.com/AtlasOmnia)
- **Source:** [AtlasOmnia/donna-starter](https://github.com/AtlasOmnia/donna-starter)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-atlasomnia-donna-starter-notion
- Seller: https://agentstack.voostack.com/s/atlasomnia
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
