---
name: notion
description: Use when working with Notion - creating, reading, updating pages and databases via the Notion API. Activates whenever the user mentions Notion, refers to a Notion URL, or asks to sync content to/from a knowledge base. Requires NOTION_API_KEY env var.
tools:
  - Read
  - Write
  - WebFetch
---

# Notion Integration Skill

Use when the user asks to create, read, update or query Notion pages and databases via the Notion REST API.

## When to use

Activate this skill whenever the user:
- Mentions Notion explicitly ("update my Notion DB", "create a Notion page").
- Pastes a Notion URL (e.g. `https://www.notion.so/...`) and asks to read or modify content.
- Asks to keep a knowledge base in sync between code/specs and Notion.
- Requests creating recurring artifacts (meeting notes, project pages, content briefs) in Notion.

Do NOT activate this skill when the user only mentions Notion in passing without asking for an action.

## Required environment

- `NOTION_API_KEY` - integration token from https://www.notion.so/profile/integrations
- The integration must be added to the parent page or database (Share -> Add connections -> select your integration).

If `NOTION_API_KEY` is missing, stop and ask the user to add it before doing any work.

## Rules

1. **Read before write.** When updating an existing page or database, fetch the current schema first and respect the existing property names and types. Never invent property names.
2. **Block-level edits.** When updating page content, work at the block level (`PATCH /v1/blocks/{id}/children`). Do not replace the whole page.
3. **Database row creation.** Use `POST /v1/pages` with `parent: {database_id: ...}` and `properties: {...}`. Each property type has a specific shape: `title` is a list of rich text, `select` is `{name: "..."}`, `multi_select` is a list, `date` is `{start, end}`.
4. **Pagination always.** Database queries return up to 100 rows per call. Loop with `start_cursor` until `has_more=false`.
5. **Rate limits.** Notion limits to ~3 requests/second. Insert a 350ms delay between writes when bulk-creating rows.
6. **Hebrew/RTL content.** When writing Hebrew text, set `text.content` directly and add `annotations: {color: "default"}`. Notion handles RTL automatically based on character direction.
7. **No public sharing.** Never expose page IDs, database IDs, or the API key in user-facing output. Reference pages by title only.

## Examples

### Create a project brief page in a database

```python
import os, requests, time

NOTION_API_KEY = os.environ["NOTION_API_KEY"]
DB_ID = "<database-id-from-url>"

headers = {
    "Authorization": f"Bearer {NOTION_API_KEY}",
    "Notion-Version": "2022-06-28",
    "Content-Type": "application/json",
}

payload = {
    "parent": {"database_id": DB_ID},
    "properties": {
        "Name": {"title": [{"text": {"content": "פרויקט בדיקת איכות תוכן"}}]},
        "Status": {"select": {"name": "Active"}},
        "Owner": {"people": [{"id": "<user-id>"}]},
        "Due": {"date": {"start": "2026-05-15"}},
    },
    "children": [
        {
            "type": "heading_2",
            "heading_2": {"rich_text": [{"text": {"content": "Goal"}}]},
        },
        {
            "type": "paragraph",
            "paragraph": {"rich_text": [{"text": {"content": "..."}}]},
        },
    ],
}
r = requests.post("https://api.notion.com/v1/pages", headers=headers, json=payload, timeout=30)
r.raise_for_status()
page_id = r.json()["id"]
```

### Query a database with filters and pagination

```python
def query_db(db_id, filter_obj):
    cursor = None
    rows = []
    while True:
        body = {"filter": filter_obj, "page_size": 100}
        if cursor:
            body["start_cursor"] = cursor
        r = requests.post(
            f"https://api.notion.com/v1/databases/{db_id}/query",
            headers=headers,
            json=body,
            timeout=30,
        )
        r.raise_for_status()
        data = r.json()
        rows.extend(data["results"])
        if not data.get("has_more"):
            break
        cursor = data.get("next_cursor")
        time.sleep(0.35)
    return rows
```

### Bulk-update a status column

```python
for page in rows:
    notion_page_id = page["id"]
    requests.patch(
        f"https://api.notion.com/v1/pages/{notion_page_id}",
        headers=headers,
        json={"properties": {"Status": {"select": {"name": "Done"}}}},
        timeout=30,
    )
    time.sleep(0.35)
```

## Output format

When the skill finishes, summarize what was created or updated in plain Hebrew with one short bullet per artifact:
- `[created] page "..." in database "Content Briefs"`
- `[updated] property "Status" on 3 rows`

Never include page IDs or database IDs in the summary.

## Failure modes to handle

- **401 unauthorized:** the integration is not added to the page/database. Ask the user to share the page with the integration.
- **404 object_not_found:** wrong ID, or the integration was removed. Ask the user to re-share.
- **429 rate_limited:** wait 1 second and retry once. If still 429, surface the error.
- **400 validation_error:** show the exact `message` field to the user; do not guess fixes.