원클릭으로
notion
Notion API for creating and managing pages, databases, and blocks.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Notion API for creating and managing pages, databases, and blocks.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Browse, search, and install skills from the CrawHub skill marketplace.
Agent Messaging (ClawMeets). Send, read, list, and delete messages via the ClawMeets agent-to-agent protocol.
Expose a local port to the public internet using ngrok. Use when the user wants a public URL for a locally running web service.
Create or update MMClaw skills. Use this when the user wants to add new capabilities, automate a specific tool, or define complex multi-step workflows for the agent.
Search the web using a configured search provider. Use when the user explicitly asks to search, look up current information, or needs real-time data such as news, prices, or recent events.
Scan a directory for sensitive files before serving it over HTTP. Always run this before using the file-server skill.
| name | notion |
| description | Notion API for creating and managing pages, databases, and blocks. |
| homepage | https://developers.notion.com |
| metadata | {"mmclaw":{"emoji":"📝","requires":{"config":["skill-config/notion.json"]}}} |
Use the Notion API to create/read/update pages, databases, and blocks.
$MMCLAW_WORKSPACE/skill-config/notion.json
{
"auth": {
"api_key": "ntn_your_key_here"
},
"defaults": {
"database_name": "MMClaw"
}
}
api_key: your Notion integration token (starts with ntn_ or secret_)defaults.database_name: the name of the database where pages are created when the user doesn't specify a locationBefore any API call, run this check:
CONFIG_FILE="${MMCLAW_WORKSPACE:-$HOME/.mmclaw}/skill-config/notion.json"
if [ ! -f "$CONFIG_FILE" ]; then
echo "NOT_CONFIGURED"
exit 1
fi
NOTION_KEY=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['auth']['api_key'])" 2>/dev/null)
DEFAULT_DB_NAME=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE')).get('defaults', {}).get('database_name', ''))" 2>/dev/null)
if [ -z "$NOTION_KEY" ] || [[ "$NOTION_KEY" != ntn_* && "$NOTION_KEY" != secret_* ]]; then
echo "INVALID_KEY"
exit 1
fi
if [ -z "$DEFAULT_DB_NAME" ]; then
echo "NO_DEFAULT_DB"
exit 1
fi
If NOT_CONFIGURED, stop and return:
⚠️ Notion is not configured.
Please create
$MMCLAW_WORKSPACE/skill-config/notion.json:{ "auth": { "api_key": "ntn_your_key_here" }, "defaults": { "database_name": "MMClaw" } }Step 1 — Get an API key:
- Go to https://notion.so/my-integrations
- Click "New integration", give it a name (e.g. "MMClaw")
- Copy the token (starts with
ntn_orsecret_)- Paste it as
auth.api_keyStep 2 — Create a default database:
- In Notion, create a new database (e.g. name it "MMClaw")
- Set
defaults.database_namein config to the database name (e.g."MMClaw")Step 3 — Grant Content Access:
- Go to https://notion.so/my-integrations → select your integration
- Click "Edit Integration" → "Content Access" → "Edit Access"
- Under "Private", check the database you just created
If INVALID_KEY, stop and return:
⚠️ Notion API key looks invalid.
The key in
$MMCLAW_WORKSPACE/skill-config/notion.jsonshould start withntn_orsecret_.
If NO_DEFAULT_DB, do NOT ask the user for a UUID and do NOT loop asking for the name repeatedly. Instead:
DB_NAME="USER_PROVIDED_NAME"
# Use empty query to list all accessible data sources, then match by name
SEARCH_RESULT=$(curl -s -X POST "https://api.notion.com/v1/search" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"filter": {"value": "data_source", "property": "object"}}')
FOUND_ID=$(echo "$SEARCH_RESULT" | python3 -c "
import json, sys
target = '$DB_NAME'.lower()
results = json.load(sys.stdin).get('results', [])
match = next((r['id'] for r in results if (r.get('title') or [{}])[0].get('plain_text','').lower() == target), '')
print(match)
")
FOUND_ID is not empty → save the name to config:python3 << 'EOF'
import json, os
path = os.environ.get('MMCLAW_WORKSPACE', os.path.expanduser('~/.mmclaw')) + '/skill-config/notion.json'
with open(path, encoding="utf-8") as f:
config = json.load(f)
config.setdefault("defaults", {})["database_name"] = "$DB_NAME"
with open(path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2, ensure_ascii=False)
print("Saved database_name:", config["defaults"]["database_name"])
EOF
⚠️ Could not find that database. This is likely a permissions issue.
Please grant Content Access first:
- Go to https://notion.so/my-integrations → select your MMClaw integration
- Click "Edit Integration" → "Content Access" → "Edit Access" → check the database
Then tell me the database name again.
NOTION_KEY=$(python3 -c "import json; print(json.load(open('${MMCLAW_WORKSPACE:-$HOME/.mmclaw}/skill-config/notion.json'))['auth']['api_key'])")
DEFAULT_DB_NAME=$(python3 -c "import json; print(json.load(open('${MMCLAW_WORKSPACE:-$HOME/.mmclaw}/skill-config/notion.json'))['defaults']['database_name'])")
Important: In API version
2025-09-03, most database operations require adata_source_id, not thedatabase_id. Always run the Discovery step first when working with a database.
Before querying or writing to a database, resolve the name to a data_source_id.
IMPORTANT: Always use an empty query ({} body with only filter) to list ALL databases, then match by name in Python. Never pass the database name as query parameter — Notion's search is fuzzy and unreliable for exact matching.
# List ALL accessible data sources (empty query = return all)
SEARCH_RESULT=$(curl -s -X POST "https://api.notion.com/v1/search" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"filter": {"value": "data_source", "property": "object"}}')
# Match by name (case-insensitive) — note: no quotes around EOF so $DEFAULT_DB_NAME expands
DATA_SOURCE_ID=$(echo "$SEARCH_RESULT" | python3 -c "
import json, sys
target = '$DEFAULT_DB_NAME'.lower()
results = json.load(sys.stdin).get('results', [])
match = next((r['id'] for r in results if (r.get('title') or [{}])[0].get('plain_text','').lower() == target), '')
print(match)
")
If
DATA_SOURCE_IDis still empty after the above, print all available names for debugging:echo "$SEARCH_RESULT" | python3 -c " import json, sys for r in json.load(sys.stdin).get('results', []): print(r.get('title', [{}])[0].get('plain_text','(untitled)'), '->', r['id']) "
If DATA_SOURCE_ID is empty → the database is not accessible. See the permissions guidance in the Default Parent Behavior section.
Use DATA_SOURCE_ID for all subsequent calls.
If the user does NOT specify a location:
data_source_id from the default database (Discovery step above)MMClaw-{timestamp} (format: YYYYMMDD-HHMMSS)TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
DATA_SOURCE_ID="..." # from Discovery step
# Step 1: create the page
PAGE=$(curl -s -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"type": "data_source_id", "data_source_id": "'"$DATA_SOURCE_ID"'"},
"properties": {
"Name": {"title": [{"text": {"content": "MMClaw-'"$TIMESTAMP"'"}}]}
}
}')
PAGE_ID=$(echo $PAGE | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
# Step 2: append user content as blocks
curl -X PATCH "https://api.notion.com/v1/blocks/$PAGE_ID/children" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"children": [
{"object": "block", "type": "paragraph", "paragraph": {"rich_text": [{"text": {"content": "..."}}]}}
]
}'
If the user specifies a location (e.g. "put it in my Tasks database"):
data_source_id and use it as parentIf search returns empty results (no databases found at all), this almost always means the integration has no Content Access granted — not that the database doesn't exist. Stop and return:
⚠️ MMClaw cannot find any Notion databases. This is likely a permissions issue, not a missing database.
Please grant Content Access:
- Go to https://notion.so/my-integrations → select your MMClaw integration
- Click "Edit Integration" → "Content Access" → "Edit Access"
- Under "Private", check the database you want MMClaw to access
After completing the above, tell me the database name again.
If a specific database returns 403, stop and return:
⚠️ MMClaw doesn't have access to that database.
To grant access:
- Go to https://notion.so/my-integrations → select your MMClaw integration
- Click "Edit Integration" → "Content Access" → "Edit Access" → check the database
Never ask the user to provide raw UUIDs or find IDs from URLs.
curl -X POST "https://api.notion.com/v1/search" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"query": "database name"}'
Filter by data source only:
-d '{"query": "name", "filter": {"value": "data_source", "property": "object"}}'
Get a page:
curl "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03"
Create a page in a database:
curl -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"type": "data_source_id", "data_source_id": "xxx"},
"properties": {
"Name": {"title": [{"text": {"content": "Page Title"}}]}
}
}'
Update page properties:
curl -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"properties": {"Status": {"select": {"name": "Done"}}}}'
Query a data source:
curl -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"filter": {"property": "Status", "select": {"equals": "Active"}},
"sorts": [{"property": "Date", "direction": "descending"}]
}'
Retrieve a data source (schema/properties):
curl "https://api.notion.com/v1/data_sources/{data_source_id}" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03"
Create a database:
curl -X POST "https://api.notion.com/v1/databases" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"page_id": "xxx"},
"is_inline": true,
"title": [{"text": {"content": "My Database"}}],
"initial_data_source": {
"properties": {
"Name": {"title": {}},
"Status": {"select": {"options": [{"name": "Todo"}, {"name": "Done"}]}},
"Date": {"date": {}}
}
}
}'
Update database schema (use data source endpoint):
curl -X PATCH "https://api.notion.com/v1/data_sources/{data_source_id}" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"properties": {"New Column": {"checkbox": {}}, "Old Column": null}}'
Update database attributes (title, icon, is_inline):
curl -X PATCH "https://api.notion.com/v1/databases/{database_id}" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{"title": [{"text": {"content": "New Title"}}]}'
Get page blocks:
curl "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03"
Append blocks:
curl -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"children": [
{"object": "block", "type": "paragraph", "paragraph": {"rich_text": [{"text": {"content": "Hello world"}}]}},
{"object": "block", "type": "heading_2", "heading_2": {"rich_text": [{"text": {"content": "Section Title"}}]}},
{"object": "block", "type": "bulleted_list_item", "bulleted_list_item": {"rich_text": [{"text": {"content": "List item"}}]}}
]
}'
| Type | Format |
|---|---|
| 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": "a@b.com"} | |
| Relation | {"relation": [{"id": "data_source_id"}]} |
| HTTP Status | Cause | Action |
|---|---|---|
| 401 | Invalid API key | Check auth.api_key in config |
| 403 | Database not accessible | Guide user to grant Content Access: https://notion.so/my-integrations → "Edit Integration" → "Content Access" → "Edit Access" |
| 404 | Wrong ID | Verify the ID; check if deleted |
| 429 | Rate limit | Wait and retry; ~3 req/sec average |
| 400 | Malformed body | Check property types; ensure data_source_id is used where required |
is_inline: true embeds a database inside a pagedata_sources[0] is the default