Skill: jeffreymaomao/notion-tools (version 1.0.2)

--- name: notion-tools description: Use for tasks that read or modify Notion pages, data sources, or blocks via the Notion API. homepage: https://developers.notion.com metadata: {"clawdbot":{"emoji":"📝"}} --- # notion-tools Use this skill when the user wants to read or modify Notion content through the Notion API. This skill is for operational API work, not general product explanation. Prefer the workflow below instead of inventing request shapes from memory. ## When To Use Use this skill for: - Finding a page, database, or data source in a Notion workspace - Reading page properties or page block content - Creating pages inside an existing database - Querying a data source with filters or sorts - Updating page properties - Trashing or restoring a page - Appending block content to a page Do not use this skill for: - Changing Notion UI-only settings such as saved views, view filters, or layout configuration - Tasks that require browser automation instead of the public API ## Quick Checks Before making requests, confirm: - Prefer `NOTION_KEY` if the runtime already provides it - If `NOTION_KEY` is unset in a local shell workflow, optionally load it from `~/.config/notion/api_key` - The target page or database has been shared with the integration - The caller understands that this skill assumes `Notion-Version: 2025-09-03` If the environment is not ready, read `references/setup.md`. ## Working Rules Always: - Start with search unless the user already provided a verified page ID or data source ID - Read before writing when modifying an existing page, block tree, or database-backed item - Inspect the live schema before creating or updating page properties in a database - Keep secrets out of output; never print the full API key back to the user - Read secrets only when needed to make a Notion API request, not during unrelated planning or explanation Do not: - Assume a property named `Name`, `Status`, or `Date` exists without checking - Assume a database ID and a data source ID are interchangeable - Overwrite page content when the task only requires appending blocks ## Workflow ### 1. Find the target Use search first for pages and data sources. If the result is a database-like object, record both IDs when present: - `id` or `database_id` for page creation parents - `data_source_id` for querying via `/v1/data_sources/{id}/query` See `references/examples.md` for the search request template. ### 2. Inspect before mutation Read the current object before writing: - `GET /v1/pages/{page_id}` for page properties - `GET /v1/blocks/{page_id}/children` for page content - `GET /v1/data_sources/{data_source_id}` for schema inspection See `references/examples.md` for request templates. ### 3. Create or update with the live schema Common write paths: - `POST /v1/pages` to create a page in an existing database - `PATCH /v1/pages/{page_id}` to update page properties - `PATCH /v1/pages/{page_id}` with `in_trash: true|false` to trash or restore a page - `PATCH /v1/blocks/{page_id}/children` to append blocks Before writing properties, confirm the exact property names and types from the live schema. Do not describe trashing a page as permanent deletion; this flow moves the page into or out of trash. See: - `references/examples.md` for create, update, trash, restore, append, and query payloads - `references/property-patterns.md` for common property value shapes - `references/blocks.md` for equation blocks, inline code, code blocks, and table-cell formatting limits ### 4. Query database-backed content through the data source API Use `/v1/data_sources/{data_source_id}/query` for filters, sorts, and pagination. If results are paginated, continue with `start_cursor` from the previous response. See `references/examples.md` for query payloads. ## API Notes For This Version This skill assumes `Notion-Version: 2025-09-03`. Important version-specific behavior: - Databases are queried through `/v1/data_sources/{data_source_id}/query` - Page creation still uses `parent.database_id` - Search results may expose a data source object instead of older database terminology - Page responses may include both `parent.database_id` and `parent.data_source_id` - Page deletion through the API is a trash operation via `in_trash`, not a permanent delete ## Troubleshooting If a request fails, check auth, sharing, object IDs, and schema mismatches before changing the payload. For common failure modes, read `references/troubleshooting.md`. ## Safety - Never commit the API key or paste it into project files - Prefer runtime-injected `NOTION_KEY` over reading a local token file - If a local token file is used, treat it as a convenience fallback for local shells rather than the default credential source - Prefer append operations for block content unless the user explicitly asks to replace content - If a task may touch many pages, query and inspect a small sample first - When the user request is underspecified, return the candidate pages or data sources and ask which one to modify

path: references/examples.md
content: # Examples

## Minimal request template

```bash
curl -sS "https://api.notion.com/v1/..." \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json"
```

## Search

```bash
curl -sS -X POST "https://api.notion.com/v1/search" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{"query":"page title or database name"}' | jq
```

## Read a page

```bash
curl -sS "https://api.notion.com/v1/pages/{page_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" | jq
```

## Read page content

```bash
curl -sS "https://api.notion.com/v1/blocks/{page_id}/children" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" | jq
```

## Read data source schema

```bash
curl -sS "https://api.notion.com/v1/data_sources/{data_source_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" | jq
```

## Create a page in an existing database

```bash
curl -sS -X POST "https://api.notion.com/v1/pages" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "parent": {"database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"},
    "properties": {
      "Name": {"title": [{"text": {"content": "New Item"}}]}
    }
  }' | jq
```

## Update page properties

```bash
curl -sS -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "properties": {
      "Status": {"select": {"name": "Done"}}
    }
  }' | jq
```

## Move a page to trash

```bash
curl -sS -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "in_trash": true
  }' | jq
```

## Restore a page from trash

```bash
curl -sS -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "in_trash": false
  }' | jq
```

## Append blocks to a page

```bash
curl -sS -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "children": [
      {
        "object": "block",
        "type": "paragraph",
        "paragraph": {
          "rich_text": [
            {"type": "text", "text": {"content": "Hello"}}
          ]
        }
      }
    ]
  }' | jq
```

## Query a data source

```bash
curl -sS -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {
      "property": "Status",
      "select": {"equals": "Active"}
    },
    "sorts": [
      {"property": "Date", "direction": "descending"}
    ]
  }' | jq
```

## Continue a paginated query

```bash
curl -sS -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "start_cursor": "cursor-from-previous-response"
  }' | jq
```


path: references/troubleshooting.md
content: # Troubleshooting

Use these checks before assuming the payload is wrong:

- `401 unauthorized`
  Check that `NOTION_KEY` is set correctly and the token file contains only the secret.
- `403 restricted_resource`
  The integration usually is not shared to the target page or database.
- `404 object_not_found`
  The ID may be wrong, or the integration cannot access the object.
- Validation errors on properties
  Read the current page or data source schema again and match exact property names and types.
- Query returns nothing
  Confirm you are using the `data_source_id` for queries and not only the database parent ID.


path: references/setup.md
content: # Setup

## Environment assumptions

- The examples assume macOS and `zsh`
- Prefer a runtime-provided `NOTION_KEY`
- Use `~/.config/notion/api_key` only as an optional local fallback
- Export `NOTION_VERSION=2025-09-03`

## Preferred credential flow

Use `NOTION_KEY` directly when the runtime or caller already provides it.

```bash
export NOTION_KEY="ntn_your_key_here"
export NOTION_VERSION=2025-09-03
```

Read the secret only when a Notion API call is actually needed, and never echo the full token back to the user.

## Optional local fallback setup

If `NOTION_KEY` is not already available in a local shell workflow, keep a fallback token file:

```bash
mkdir -p ~/.config/notion
echo "ntn_your_key_here" > ~/.config/notion/api_key
```

## Load variables

```bash
export NOTION_VERSION=2025-09-03

if [ -z "${NOTION_KEY:-}" ] && [ -f ~/.config/notion/api_key ]; then
  export NOTION_KEY="$(cat ~/.config/notion/api_key)"
fi
```

This preserves `NOTION_KEY` when it is already present and touches the local file only as a fallback.

## JSON inspection

If `jq` is installed, use it to inspect JSON responses.

On macOS:

```bash
brew install jq
```

If `jq` is unavailable, run the same `curl` commands without `| jq`, or use `python3 -m json.tool` only when pretty printing is needed.


path: references/blocks.md
content: # Blocks And Formatting Edge Cases

## Equation blocks

When writing an equation block, set `equation.expression` to the intended LaTeX content, not to a double-escaped display string.

Rules:
- Use normal LaTeX content such as `\int_0^1 x^2 \, dx = \frac{1}{3}`
- In a JSON payload, backslashes appear as `\\` because JSON escapes them; that does not mean the stored formula should contain doubled backslashes
- When reading an equation block back, distinguish between the JSON representation and the actual LaTeX content
- When reporting the value to the user, show the actual LaTeX content rather than the JSON-escaped form
- If a formula appears with doubled backslashes after writing, treat that as an escaping mistake and rewrite the block with the intended LaTeX expression

Example:

```bash
curl -sS -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: $NOTION_VERSION" \
  -H "Content-Type: application/json" \
  -d '{
    "children": [
      {
        "object": "block",
        "type": "equation",
        "equation": {
          "expression": "\\int_0^1 x^2 \\, dx = \\frac{1}{3}"
        }
      }
    ]
  }' | jq
```

## Code content and table cells

When writing code-like content, distinguish between plain text that looks like code and rich text that is actually marked as code.

Rules:
- Backticks such as `` `x = 42` `` are only literal characters unless the API payload sets rich text annotations
- For inline code, use a rich text object with `"annotations": {"code": true}` instead of embedding backticks in the text
- Code blocks should be written as `type: "code"` blocks, not as paragraph text with triple backticks
- Table cells accept rich text arrays, but their UI rendering may differ from paragraph blocks
- In table cells, `annotations.code = true` can work for inline code styling; raw backticks usually render as normal text
- If the user wants reliable visible code formatting, prefer a paragraph with inline code or a dedicated code block over a table cell

When reading results back to the user:
- Do not claim inline code was created just because the text contains backticks
- Confirm whether the payload used `annotations.code = true`
- If the styling matters, mention that Notion UI rendering inside table cells may be more limited than in paragraphs

Example inline code rich text:

```json
{
  "type": "text",
  "text": {
    "content": "x = 42"
  },
  "annotations": {
    "code": true
  }
}
```

Example code block:

```json
{
  "object": "block",
  "type": "code",
  "code": {
    "language": "python",
    "rich_text": [
      {
        "type": "text",
        "text": {
          "content": "print('hello')"
        }
      }
    ]
  }
}
```


path: references/property-patterns.md
content: # Property Patterns

Use these shapes only after confirming the property exists in the target schema:

- 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":"2024-01-15","end":"2024-01-16"}}`
- Checkbox: `{"checkbox":true}`
- Number: `{"number":42}`
- URL: `{"url":"https://..."}`
- Email: `{"email":"[REDACTED_SECRET]"}`
- Relation: `{"relation":[{"id":"page_id"}]}`


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)