AgentSkillsCN

notion

Notion API 与托管 OAuth 集成。支持查询数据库、创建与更新页面、管理区块。当用户希望与 Notion 工作空间、数据库或页面交互时,可使用此技能。对于其他第三方应用,请使用 API 网关技能(https://clawhub.ai/byungkyu/api-gateway)。

SKILL.md
--- frontmatter
name: notion
description: |
  Notion API integration with managed OAuth. Query databases, create and update pages, manage blocks. Use this skill when users want to interact with Notion workspaces, databases, or pages. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
compatibility: Requires network access and valid Maton API key
metadata:
  author: maton
  version: "1.0"

Notion

Access the Notion API with managed OAuth authentication. Query databases, create pages, manage blocks, and search your workspace.

Quick Start

bash
# Search for pages
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'query': 'meeting notes'}).encode()
req = urllib.request.Request('https://gateway.maton.ai/notion/v1/search', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('Notion-Version', '2025-09-03')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Base URL

code
https://gateway.maton.ai/notion/{native-api-path}

Replace {native-api-path} with the actual Notion API endpoint path. The gateway proxies requests to api.notion.com and automatically injects your OAuth token.

Required Headers

All Notion API requests require the version header:

code
Notion-Version: 2025-09-03

Authentication

All requests require the Maton API key in the Authorization header:

code
Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

bash
export MATON_API_KEY="YOUR_API_KEY"

Getting Your API Key

  1. Sign in or create an account at maton.ai
  2. Go to maton.ai/settings
  3. Copy your API key

Connection Management

Manage your Notion OAuth connections at https://ctrl.maton.ai.

List Connections

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=notion&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Create Connection

bash
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'notion'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Get Connection

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Response:

json
{
  "connection": {
    "connection_id": "21fd90f9-5935-43cd-b6c8-bde9d915ca80",
    "status": "ACTIVE",
    "creation_time": "2025-12-08T07:20:53.488460Z",
    "last_updated_time": "2026-01-31T20:03:32.593153Z",
    "url": "https://connect.maton.ai/?session_token=...",
    "app": "notion",
    "metadata": {}
  }
}

Open the returned url in a browser to complete OAuth authorization.

Delete Connection

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Specifying Connection

If you have multiple Notion connections, specify which one to use with the Maton-Connection header:

bash
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'query': 'meeting notes'}).encode()
req = urllib.request.Request('https://gateway.maton.ai/notion/v1/search', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('Notion-Version', '2025-09-03')
req.add_header('Maton-Connection', '21fd90f9-5935-43cd-b6c8-bde9d915ca80')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

If omitted, the gateway uses the default (oldest) active connection.

Key Concept: Databases vs Data Sources

In API version 2025-09-03, databases and data sources are separate:

ConceptUse For
DatabaseCreating databases, getting data source IDs
Data SourceQuerying, updating schema, updating properties

Use GET /databases/{id} to get the data_sources array, then use /data_sources/ endpoints:

json
{
  "object": "database",
  "id": "abc123",
  "data_sources": [
    {"id": "def456", "name": "My Database"}
  ]
}

API Reference

Search

Search for pages:

bash
POST /notion/v1/search
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "query": "meeting notes",
  "filter": {"property": "object", "value": "page"}
}

Search for data sources:

bash
POST /notion/v1/search
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "filter": {"property": "object", "value": "data_source"}
}

Data Sources

Get Data Source

bash
GET /notion/v1/data_sources/{dataSourceId}
Notion-Version: 2025-09-03

Query Data Source

bash
POST /notion/v1/data_sources/{dataSourceId}/query
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "filter": {
    "property": "Status",
    "select": {"equals": "Active"}
  },
  "sorts": [
    {"property": "Created", "direction": "descending"}
  ],
  "page_size": 100
}

Update Data Source

bash
PATCH /notion/v1/data_sources/{dataSourceId}
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "title": [{"type": "text", "text": {"content": "Updated Title"}}],
  "properties": {
    "NewColumn": {"rich_text": {}}
  }
}

Databases

Get Database

bash
GET /notion/v1/databases/{databaseId}
Notion-Version: 2025-09-03

Create Database

bash
POST /notion/v1/databases
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "parent": {"type": "page_id", "page_id": "PARENT_PAGE_ID"},
  "title": [{"type": "text", "text": {"content": "New Database"}}],
  "properties": {
    "Name": {"title": {}},
    "Status": {"select": {"options": [{"name": "Active"}, {"name": "Done"}]}}
  }
}

Pages

Get Page

bash
GET /notion/v1/pages/{pageId}
Notion-Version: 2025-09-03

Create Page

bash
POST /notion/v1/pages
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "parent": {"page_id": "PARENT_PAGE_ID"},
  "properties": {
    "title": {"title": [{"text": {"content": "New Page"}}]}
  }
}

Create Page in Data Source

bash
POST /notion/v1/pages
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "parent": {"data_source_id": "DATA_SOURCE_ID"},
  "properties": {
    "Name": {"title": [{"text": {"content": "New Page"}}]},
    "Status": {"select": {"name": "Active"}}
  }
}

Update Page Properties

bash
PATCH /notion/v1/pages/{pageId}
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "properties": {
    "Status": {"select": {"name": "Done"}}
  }
}

Update Page Icon

bash
PATCH /notion/v1/pages/{pageId}
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "icon": {"type": "emoji", "emoji": "🚀"}
}

Archive Page

bash
PATCH /notion/v1/pages/{pageId}
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "archived": true
}

Blocks

Get Block Children

bash
GET /notion/v1/blocks/{blockId}/children
Notion-Version: 2025-09-03

Append Block Children

bash
PATCH /notion/v1/blocks/{blockId}/children
Content-Type: application/json
Notion-Version: 2025-09-03

{
  "children": [
    {
      "object": "block",
      "type": "paragraph",
      "paragraph": {
        "rich_text": [{"type": "text", "text": {"content": "New paragraph"}}]
      }
    }
  ]
}

Delete Block

bash
DELETE /notion/v1/blocks/{blockId}
Notion-Version: 2025-09-03

Users

List Users

bash
GET /notion/v1/users
Notion-Version: 2025-09-03

Get Current User

bash
GET /notion/v1/users/me
Notion-Version: 2025-09-03

Filter Operators

  • equals, does_not_equal
  • contains, does_not_contain
  • starts_with, ends_with
  • is_empty, is_not_empty
  • greater_than, less_than

Block Types

  • paragraph, heading_1, heading_2, heading_3
  • bulleted_list_item, numbered_list_item
  • to_do, code, quote, divider

Code Examples

JavaScript

javascript
const response = await fetch('https://gateway.maton.ai/notion/v1/search', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.MATON_API_KEY}`,
    'Notion-Version': '2025-09-03'
  },
  body: JSON.stringify({ query: 'meeting' })
});

Python

python
import os
import requests

response = requests.post(
    'https://gateway.maton.ai/notion/v1/search',
    headers={
        'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
        'Notion-Version': '2025-09-03'
    },
    json={'query': 'meeting'}
)

Notes

  • All IDs are UUIDs (with or without hyphens)
  • Use GET /databases/{id} to get the data_sources array containing data source IDs
  • Creating databases requires POST /databases endpoint
  • Delete blocks returns the block with archived: true
  • IMPORTANT: When using curl commands, use curl -g when URLs contain brackets (fields[], sort[], records[]) to disable glob parsing
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.

Error Handling

StatusMeaning
400Missing Notion connection
401Invalid or missing Maton API key
429Rate limited (10 req/sec per account)
4xx/5xxPassthrough error from Notion API

Troubleshooting: Invalid API Key

When you receive a "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:

  1. Check that the MATON_API_KEY environment variable is set:
bash
echo $MATON_API_KEY
  1. Verify the API key is valid by listing connections:
bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Resources