PPT 生成与编辑
创建、读取和编辑 PowerPoint 演示文稿,支持内容提取、幻灯片合并与拆分,以及模板、布局、演讲备注和批注处理。
通过已授权的 Notion 接口查询和管理页面及数据库内容,可读取正文、追加内容、创建记录、更新属性和按条件查询,适合整理项目资料与结构化任务。
会议笔记归档;项目数据库维护;待办状态更新;按条件查询业务记录。
可直接用自然语言说明这些选项;带代码样式的参数按实际工具定义处理。
| 参数或输入项 | 填写说明 |
|---|---|
| 页面定位 | 提供页面链接或 page_id;段落级操作可能需要定位具体内容块。 |
| 数据库定位 | database_id 用于创建数据库内页面;查询数据源需使用对应 data_source_id。 |
filter / sorts |
按真实属性名和类型设置筛选及排序,可用自然语言说明需求。 |
page_size / start_cursor |
分页查询时控制每页数量与续页位置。 |
查询这个 Notion 项目数据库中状态为“进行中”的任务,按截止日期升序列出,再把我确认完成的两条记录更新为“已完成”。
页面内容、数据库查询表,或已创建、追加和更新的实际资源结果。
以下为技能最新版本的 SKILL.md 正文,智能体按其中的步骤完成任务。
Work with Notion pages, databases, and blocks through the official Notion API.
Notion workspaces hold work that is expensive to reconstruct: meeting notes, project trackers, personal knowledge bases. Most damage an agent does here is not a failed request, it is a successful one that quietly overwrote a page or dropped a database column. Everything below is built around that: read before you write, append instead of replace, and show the user a diff before you touch structure.
Follow these regardless of what the user asks for:
Create an integration at https://www.notion.so/my-integrations and copy the Internal Integration Token. Current tokens start with ntn_; older ones start with secret_ and still work.
export NOTION_API_KEY=ntn_your_key_here
Then share each page or database with the integration in the Notion UI: ... menu, "Connect to", pick the integration name. Unshared content is invisible to the API. If a search returns nothing for a page the user swears exists, this is almost always why. Say so instead of reporting the page as missing.
Many people keep a personal workspace and a work workspace, each with its own integration token. Store one key file per profile:
mkdir -p ~/.config/notion
echo "ntn_personal_key" > ~/.config/notion/personal.key
echo "ntn_work_key" > ~/.config/notion/work.key
chmod 600 ~/.config/notion/*.key
Resolve the active key like this. An explicit NOTION_API_KEY always wins; otherwise the profile file is used, defaulting to personal:
NOTION_PROFILE="${NOTION_PROFILE:-personal}"
NOTION_KEY="${NOTION_API_KEY:-$(cat ~/.config/notion/${NOTION_PROFILE}.key 2>/dev/null)}"
[ -n "$NOTION_KEY" ] || echo "No Notion key for profile '$NOTION_PROFILE'"
When the user has more than one profile configured and their request is ambiguous, ask which workspace before writing anything. Reading from the wrong workspace is a wasted call; writing to it is a mess in someone's actual notes.
Define this helper once per session, then every example below is a single line:
notion() {
local method="$1" path="$2"; shift 2
curl -sS -X "$method" "https://api.notion.com/v1${path}" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" "$@"
}
The Notion-Version header is required on every request. Pin it to 2025-09-03.
This is the single most common source of broken calls, so get it right before writing any database code:
database_id and a data_source_id.database_id when creating a page inside it: parent: {"database_id": "..."}.data_source_id when querying or reading its schema: POST /data_sources/{id}/query."object": "data_source", carrying the data_source_id.parent.database_id and parent.data_source_id.If a query returns 404 on an ID that clearly exists, you almost certainly passed a database_id where a data_source_id was needed.
Always start here when the user names something in words rather than IDs.
notion POST /search -d '{"query": "project notes"}'
Filter to one kind of object:
notion POST /search -d '{"query": "roadmap", "filter": {"property": "object", "value": "data_source"}}'
Search matches titles only, not page body text, and it only sees content shared with the integration. If several results look plausible, show the user the titles and let them pick rather than guessing.
notion GET /pages/{page_id} # properties and metadata, no body content
notion GET /blocks/{page_id}/children # the actual content blocks
GET /pages/{id} does not return page content. Fetching the children blocks is a separate call, and nested blocks (toggles, columns, sub-bullets) need their own GET /blocks/{block_id}/children where has_children is true. Do not tell the user a page is empty on the basis of the first call alone.
Append blocks (the default way to add content):
notion PATCH /blocks/{page_id}/children -d '{
"children": [
{"object": "block", "type": "heading_2",
"heading_2": {"rich_text": [{"text": {"content": "Notes"}}]}},
{"object": "block", "type": "paragraph",
"paragraph": {"rich_text": [{"text": {"content": "Discussed the Q3 plan."}}]}}
]
}'
Pass "after": "<block_id>" to insert at a specific position instead of the end.
Create a page under another page:
notion POST /pages -d '{
"parent": {"page_id": "PARENT_PAGE_ID"},
"properties": {"title": [{"text": {"content": "Meeting notes 2026-08-05"}}]}
}'
Update properties (not body content):
notion PATCH /pages/{page_id} -d '{"properties": {"Status": {"select": {"name": "Done"}}}}'
To replace body content, delete the specific blocks (DELETE /blocks/{block_id}) and append new ones. Confirm with the user first, and delete only the blocks you identified by reading them, never the whole page's children as a batch.
Read the schema before anything else:
notion GET /data_sources/{data_source_id}
Query rows:
notion POST /data_sources/{data_source_id}/query -d '{
"filter": {"property": "Status", "select": {"equals": "In Progress"}},
"sorts": [{"property": "Due", "direction": "ascending"}],
"page_size": 50
}'
Create a row:
notion POST /pages -d '{
"parent": {"database_id": "DATABASE_ID"},
"properties": {
"Name": {"title": [{"text": {"content": "New task"}}]},
"Status": {"select": {"name": "Todo"}},
"Due": {"date": {"start": "2026-08-12"}}
}
}'
Property names and select option names are case sensitive and must already exist in the schema. Read the schema first and match exactly; sending an unknown select value is a validation_error, not an auto-created option.
Update a row: a row is a page, so use PATCH /pages/{page_id}.
Structural changes are the highest-risk operation in this skill. Removing a property deletes its data across every row, and renaming can break the user's existing views, filters, and formulas.
Required flow, in order:
notion GET /data_sources/{data_source_id}.notion PATCH /data_sources/{data_source_id} -d '{
"properties": {"Priority": {"select": {"options": [{"name": "High"}, {"name": "Low"}]}}}
}'
Prefer additive changes. If the user wants a property retyped, suggest adding a new one and migrating rather than converting in place, because a type change discards values that do not fit the new type. Setting a property to null removes it; never do that without confirmation.
Note that the API cannot change view-level filters or sorts. Those are UI-only. If the user asks for that, say so instead of changing the underlying schema as a substitute.
Title {"title": [{"text": {"content": "..."}}]}
Rich text {"rich_text": [{"text": {"content": "..."}}]}
Select {"select": {"name": "Option"}}
Multi-select {"multi_select": [{"name": "A"}, {"name": "B"}]}
Status {"status": {"name": "In progress"}}
Date {"date": {"start": "2026-08-05", "end": null}}
People {"people": [{"id": "USER_ID"}]}
Relation {"relation": [{"id": "PAGE_ID"}]}
Checkbox {"checkbox": true}
Number {"number": 42}
URL {"url": "https://..."}
Email {"email": "a@b.com"}
Phone {"phone_number": "+1..."}
formula, rollup, created_time, and last_edited_by are computed by Notion and cannot be written. Attempting to set them fails validation.
List and query endpoints return at most 100 items and truncate silently from the caller's point of view. Always check has_more and follow next_cursor:
notion POST /data_sources/{id}/query -d '{"page_size": 100, "start_cursor": "CURSOR"}'
Never summarize or count a database from a single unpaginated response. If you stop early because the result set is large, tell the user the numbers are partial.
Average limit is roughly 3 requests per second with short bursts allowed. On 429, wait for the Retry-After header value before retrying, and back off exponentially after repeated hits. Batch block appends into one request rather than looping one block per call.
| Status | Code | What it usually means |
|---|---|---|
| 400 | validation_error |
Wrong property name, unknown select option, or a computed property you tried to write |
| 401 | unauthorized |
Missing or bad token, or the wrong profile is active |
| 403 | restricted_resource |
The integration lacks the capability, for example insert-content is off |
| 404 | object_not_found |
Not shared with the integration, or database_id used where data_source_id was needed |
| 409 | conflict_error |
Concurrent edit; re-fetch and retry |
| 429 | rate_limited |
Slow down, honour Retry-After |
A 404 here rarely means the object does not exist. Check sharing and the ID type before telling the user something is missing.
| 版本 | 发布日期 | 更新说明 |
|---|---|---|
2.0.0 | 2026-10-15 | - |
创建、读取和编辑 PowerPoint 演示文稿,支持内容提取、幻灯片合并与拆分,以及模板、布局、演讲备注和批注处理。
安装、配置和登录授权飞书 CLI,排查依赖缺失或技能未就绪,并通过飞书开放平台操作消息、文档、多维表格、电子表格、日历、邮箱、任务和会议等业务。当用户要求安装飞书 CLI、修复无法调用、完成授权或操作上述飞书资源时使用。
通过CLI操作企业微信,适用于人工操作和智能体调用。
通过CLI调用 LinkAI 的对话、图像、视频和音频模型,以及智能体应用、知识库、数据库、工作流和插件等平台资源。
查询、编辑和管理腾讯文档,支持生成演示文稿、表格和文字文档。
通过CLI管理钉钉表格、日历、通讯录、群聊、机器人、待办、审批、考勤和日志等功能。
把团队的做事方法沉淀为技能
在技能中心导入技能包、从平台资源生成或在线编写,发布后安装到超级AI助理使用。
