Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
586 changes: 256 additions & 330 deletions skills/productivity/notion/SKILL.md

Large diffs are not rendered by default.

218 changes: 218 additions & 0 deletions skills/productivity/notion/references/api-2026-03-11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# Notion API 2026-03-11 Operating Map

Sources:

- `https://developers.notion.com/openapi.json`
- `https://developers.notion.com/reference/versioning.md`
- `https://developers.notion.com/reference/changes-by-version.md`
- `https://developers.notion.com/guides/get-started/upgrade-guide-2026-03-11.md`
- `https://developers.notion.com/guides/get-started/upgrade-guide-2025-09-03.md`
- `https://developers.notion.com/reference/request-limits.md`
- `https://developers.notion.com/reference/status-codes.md`

## Required envelope

```text
Authorization: Bearer <token>
Notion-Version: 2026-03-11
Content-Type: application/json # when sending JSON body
```

Rules:

- Base URL: `https://api.notion.com`.
- REST path namespace remains `/v1`.
- Request/response bodies are JSON except file upload send endpoints, which use multipart/form-data.
- IDs are UUIDs and may be dashed or undashed.
- Properties use snake_case.
- Date/datetime values are ISO 8601.
- Use `null`, not empty string, to unset nullable strings.
- Ignore unknown response fields.

## Version cliffs

### `2025-09-03`

Databases changed shape:

- `database` is a container.
- `data_source` is a table/schema/row parent.
- Retrieve a database to discover `data_sources`.
- Retrieve/query/update the data source for schema and rows.
- Search object filter/result value is `data_source` instead of `database`.
- Page row parents and relation writes use `data_source_id`.

### `2026-03-11`

Breaking changes:

- `PATCH /v1/blocks/{id}/children`: `after` replaced by `position`.
- `archived` removed/replaced by `in_trash`.
- `transcription` block type renamed to `meeting_notes`.

## Pagination

Response shape:

```text
object: list
results: [...]
has_more: true|false
next_cursor: opaque string when has_more
```

Mechanics:

- `GET` endpoints use query parameters: `start_cursor`, `page_size`.
- `POST` endpoints use JSON body fields: `start_cursor`, `page_size`.
- `page_size` max is generally 100.
- Cursors are opaque; pass `next_cursor` back as `start_cursor` verbatim.
- Do not persist assumptions about cursor format.

Paginated surfaces include users, block children, comments, page property items, file uploads, data-source templates, views, view query results, data-source query, search, and custom emojis.

## Rate limits and sizes

Rate:

- Average 3 requests/second per connection.
- Some bursts allowed.
- 429 responses include `Retry-After` integer seconds. Wait at least that long.
- Limits may change and may vary by workspace plan in the future.

General request limits:

- 500KB maximum payload.
- 1000 block elements maximum per request.
- Many arrays of block/rich-text objects cap at 100 elements.
- Append block children caps at 100 child blocks and two nesting levels per request.

Value limits:

- Rich text `text.content`: 2000 chars.
- Rich text link URL: 2000 chars.
- Equation expression: 1000 chars.
- URL: 2000 chars.
- Email/phone: 200 chars.
- Multi-select options: 100.
- Relation entries: 100.
- People entries: 100.

## Error handling

Error body has programmatic `code` plus human `message`; message text may change without a version bump.

Important codes:

- `400 invalid_json`: invalid body JSON.
- `400 invalid_request_url`: bad URL.
- `400 invalid_request`: unsupported request.
- `400 invalid_grant`: OAuth grant/refresh issue.
- `400 validation_error`: invalid payload/parameters.
- `400 missing_version`: missing `Notion-Version`.
- `401 unauthorized`: invalid bearer token.
- `403 restricted_resource`: missing permission/capability.
- `404 object_not_found`: missing or not shared with token owner/connection.
- `409 conflict_error`: data collision or temporary storage conflict; refresh inputs and retry cautiously.
- `429 rate_limited`: respect `Retry-After`.
- `500 internal_server_error`: Notion server error.
- `502 bad_gateway`: retry with backoff.
- `503 service_unavailable`: Notion unavailable or >60s timeout; retry later.
- `503 database_connection_unavailable`: database temporarily unavailable.
- `504 gateway_timeout`: retry with backoff.

Retry policy:

- Retry 429 after `Retry-After`.
- Retry 502/503/504 with exponential backoff and jitter.
- For data-source query 503, reduce `page_size` and narrow filters/sorts.
- Do not blind-retry creates after ambiguous failures; Notion does not document idempotency keys.

## Endpoint map by owner

### Pages

- `POST /v1/pages` — create page. Parent can be page, data source, or workspace for public/PAT contexts. Body can use `markdown`, `children`/`content`, or `template` depending on mode.
- `GET /v1/pages/{page_id}` — page metadata/properties, not body blocks.
- `PATCH /v1/pages/{page_id}` — update properties/icon/cover/lock/template/content erase/trash via `in_trash`.
- `POST /v1/pages/{page_id}/move` — move regular page to page or data source parent.
- `GET /v1/pages/{page_id}/properties/{property_id}` — complete paginated property values.
- `GET /v1/pages/{page_id}/markdown` — agent-friendly page body markdown.
- `PATCH /v1/pages/{page_id}/markdown` — markdown exact updates or replace whole body.

### Blocks

- `GET /v1/blocks/{block_id}` — one block.
- `GET /v1/blocks/{block_id}/children` — first-level children only; recurse yourself.
- `PATCH /v1/blocks/{block_id}/children` — append children with `position`.
- `PATCH /v1/blocks/{block_id}` — update block fields or `in_trash`.
- `DELETE /v1/blocks/{block_id}` — trash block/page-block.
- `POST /v1/blocks/meeting_notes/query` — query meeting notes.

### Data sources and databases

- `GET /v1/databases/{database_id}` — retrieve database container and discover data-source IDs.
- `POST /v1/databases` — create database container and initial data source.
- `PATCH /v1/databases/{database_id}` — update container-level fields.
- `GET /v1/data_sources/{data_source_id}` — retrieve data source schema/table.
- `POST /v1/data_sources/{data_source_id}/query` — query rows/pages.
- `POST /v1/data_sources` — add a data source to an existing database.
- `PATCH /v1/data_sources/{data_source_id}` — update schema/source fields or move/trash data source.
- `GET /v1/data_sources/{data_source_id}/templates` — list templates.
- `POST /v1/databases/{database_id}/query` — legacy/deprecated database query endpoint.

### Views

- `GET /v1/views` — list views by database/data source.
- `POST /v1/views` — create view.
- `GET /v1/views/{view_id}` — retrieve view config.
- `PATCH /v1/views/{view_id}` — update view config.
- `DELETE /v1/views/{view_id}` — delete view.
- `POST /v1/views/{view_id}/queries` — create cached view query.
- `GET /v1/views/{view_id}/queries/{query_id}` — paginate cached query.
- `DELETE /v1/views/{view_id}/queries/{query_id}` — free cached query; idempotent.

View query results expire after about 15 minutes. You cannot add extra filters/sorts to a view query; edit the view or use data-source query.

### Comments

- `GET /v1/comments?block_id=...` — list open comments on page/block; pages are blocks.
- `POST /v1/comments` — top-level page comment or reply to existing discussion.
- `GET /v1/comments/{comment_id}` — retrieve one comment.
- `PATCH /v1/comments/{comment_id}` — update own comment body.
- `DELETE /v1/comments/{comment_id}` — delete own comment.

Comments can use `rich_text` or `markdown` body, mutually exclusive.

### Search and users

- `POST /v1/search` — title search over shared pages/data sources; not exhaustive inventory.
- `GET /v1/users` — paginated workspace users; requires user capability; PATs cannot list all users.
- `GET /v1/users/{user_id}` — retrieve a user/bot/guest in workspace.
- `GET /v1/users/me` — token's bot/current user.
- `GET /v1/custom_emojis` — paginated custom emoji list; supports exact `name` filter for name-to-ID lookup.

Custom emoji/icon notes:

- To set a custom emoji icon, use `type: "custom_emoji"` with the custom emoji `id`; do not infer IDs from markdown `:name:` syntax. Sources: official OpenAPI `customEmojiIconRequest` and `GET /v1/custom_emojis` in `https://developers.notion.com/openapi.json`.
- Native Notion icons are structured `type: "icon"` objects in current docs/SDK surfaces. Source: `https://developers.notion.com/page/changelog.md`.

### OAuth

- `POST /v1/oauth/token` — code exchange or refresh.
- `POST /v1/oauth/revoke` — revoke token.
- `POST /v1/oauth/introspect` — inspect token.

OAuth token endpoints use Basic auth (`client_id:client_secret`) in addition to request body semantics.

## Idempotency and concurrency

No idempotency-key or optimistic-concurrency header is documented in the focused official corpus.

Safe practices:

- Store Notion IDs after creates.
- For ambiguous create failures, search/read by your own external key before retrying.
- Use exact `update_content` markdown matches when editing text so drift fails validation.
- Read after writes when a downstream action depends on new state.
- Use webhooks plus narrow filters for sync instead of full polling loops.
Loading