dingtalk-document
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese钉钉文档技能
DingTalk Docs Skill
负责钉钉知识库和文档的所有操作,通过钉钉开放平台 API 实现。
API 详情见 。
references/api.mdResponsible for all operations of DingTalk knowledge base and documents, implemented through DingTalk Open Platform API.
For API details, see .
references/api.md配置管理(每次开始前必读)
Configuration Management (Must Read Before Each Start)
配置文件路径
Configuration File Path
~/.dingtalk-skills/config~/.dingtalk-skills/config本技能需要的配置
Required Configurations for This Skill
| 键 | 说明 | 来源 |
|---|---|---|
| 钉钉应用 appKey | 开放平台 → 应用管理 → 凭证信息 |
| 钉钉应用 appSecret | 同上 |
| 操作人 unionId | 见下方"为什么需要 operatorId"章节 |
| Key | Description | Source |
|---|---|---|
| DingTalk application appKey | Open Platform → Application Management → Credential Information |
| DingTalk application appSecret | Same as above |
| Operator unionId | See the "Why operatorId is required" section below |
启动流程(每次执行任务前)
Startup Process (Before Each Task Execution)
- 读取配置:检查 是否存在,解析已有键值
~/.dingtalk-skills/config - 识别缺失项:找出上表中尚未配置的键
- 一次性收集:将所有缺失项合并为一条提问,不要逐条询问,例如:
需要以下信息才能继续(已有的无需再填):
- 钉钉应用 appKey(钉钉开放平台 → 应用管理 → 凭证信息)
- 钉钉应用 appSecret
- 你的钉钉 userId 或 unionId(以便以你的身份操作文档)
- 持久化:将用户提供的值追加写入 config,后续直接读取,无需再问
- 执行任务:配置完整后开始操作
注意:/APP_KEY/APP_SECRET属于凭证,禁止在输出中完整打印,确认时仅显示前 4 位 +OPERATOR_ID。****
- Read configuration: Check if exists, parse existing key-value pairs
~/.dingtalk-skills/config - Identify missing items: Find the keys in the above table that have not been configured yet
- One-time collection: Combine all missing items into one question, do not ask one by one, for example:
The following information is required to proceed (no need to fill in existing information):
- DingTalk application appKey (DingTalk Open Platform → Application Management → Credential Information)
- DingTalk application appSecret
- Your DingTalk userId or unionId (to operate documents as your identity)
- Persistence: Append the values provided by the user to the config file, read directly in the future without asking again
- Execute task: Start operation after the configuration is complete
Note:/APP_KEY/APP_SECRETare credentials, it is forbidden to print them completely in the output, only show the first 4 digits +OPERATOR_IDwhen confirming.****
认证
Authentication
每次调用 API 前,用 appKey/appSecret 获取当次的 accessToken(有效期 2 小时):
POST https://api.dingtalk.com/v1.0/oauth2/accessToken
Content-Type: application/json
{
"appKey": "<应用 appKey>",
"appSecret": "<应用 appSecret>"
}返回:
{ "accessToken": "xxx", "expireIn": 7200 }所有后续请求均需携带请求头:
x-acs-dingtalk-access-token: <accessToken>Before each API call, use appKey/appSecret to obtain the current accessToken (valid for 2 hours):
POST https://api.dingtalk.com/v1.0/oauth2/accessToken
Content-Type: application/json
{
"appKey": "<应用 appKey>",
"appSecret": "<应用 appSecret>"
}Return:
{ "accessToken": "xxx", "expireIn": 7200 }All subsequent requests need to carry the request header:
x-acs-dingtalk-access-token: <accessToken>为什么需要 operatorId(unionId)
Why is operatorId (unionId) required
钉钉开放平台要求所有写操作(创建文档、写入内容、管理成员等)必须代表一个真实用户身份执行,而不是以匿名应用身份操作。 就是声明"这个操作是谁做的"——它会被记录到文档的变更历史,并用于权限校验。
operatorId- 值为操作人的 (钉钉跨组织唯一 ID),不是
unionId(仅组织内唯一)userId - 错误传入 userId 会导致权限报错或文档归属错误
DingTalk Open Platform requires all write operations (create documents, write content, manage members, etc.) must be performed on behalf of a real user identity, not as an anonymous application. is to declare "who performed this operation" - it will be recorded in the document change history and used for permission verification.
operatorId- The value is the operator's (DingTalk cross-organization unique ID), not
unionId(only unique within the organization)userId - Incorrectly passing userId will cause permission errors or document ownership errors
如何获取 unionId
How to get unionId
方法一:已知 userId → 换取 unionId(最常用)
userId第一步:获取旧式 (与新版 accessToken 不同,此处单独获取):
access_tokenGET https://oapi.dingtalk.com/gettoken?appkey=<appKey>&appsecret=<appSecret>返回:
{ "access_token": "xxx", "expires_in": 7200 }第二步:用 userId 查询用户详情,取出 unionId:
POST https://oapi.dingtalk.com/topapi/v2/user/get?access_token=<旧式token>
Content-Type: application/json
{ "userid": "<钉钉 userId>" }返回字段 (无下划线)即为 unionId。
result.unionid注意:(有下划线)在专属钉钉组织中可能为空,请使用result.union_id。result.unionid
方法二:机器人/消息场景
用户通过钉钉机器人或消息触发时,消息体中直接包含 字段,可直接作为 使用,无需额外查询。
senderUnionIdoperatorId方法三:直接询问用户
若上述方式不可用,在初始配置阶段询问用户提供 userId 或 unionId(均可在钉钉个人信息页查看)。获取后写入 config,后续操作无需重复获取。
Method 1: Known userId → exchange for unionId (most commonly used)
userIdStep 1: Obtain the old version (different from the new version accessToken, obtained separately here):
access_tokenGET https://oapi.dingtalk.com/gettoken?appkey=<appKey>&appsecret=<appSecret>Return:
{ "access_token": "xxx", "expires_in": 7200 }Step 2: Query user details with userId, extract unionId:
POST https://oapi.dingtalk.com/topapi/v2/user/get?access_token=<旧式token>
Content-Type: application/json
{ "userid": "<钉钉 userId>" }The returned field (no underscore) is the unionId.
result.unionidNote:(with underscore) may be empty in exclusive DingTalk organizations, please useresult.union_id.result.unionid
Method 2: Robot/message scenario
When a user triggers through a DingTalk robot or message, the message body directly contains the field, which can be directly used as without additional query.
senderUnionIdoperatorIdMethod 3: Ask the user directly
If the above methods are not available, ask the user to provide userId or unionId during the initial configuration phase (both can be viewed on the DingTalk personal information page). Write to config after obtaining, no need to obtain repeatedly for subsequent operations.
核心操作
Core Operations
1. 查询用户知识库列表
1. Query user knowledge base list
用户想查看有哪些知识库时:
GET https://api.dingtalk.com/v2.0/wiki/workspaces?operatorId=<unionId>&maxResults=20&nextToken=<分页令牌>
x-acs-dingtalk-access-token: <accessToken>如有 则继续翻页,直到无 为止。返回字段中 和 供后续操作使用。
nextTokennextTokenworkspaceIdrootNodeIdWhen the user wants to view the available knowledge bases:
GET https://api.dingtalk.com/v2.0/wiki/workspaces?operatorId=<unionId>&maxResults=20&nextToken=<分页令牌>
x-acs-dingtalk-access-token: <accessToken>If there is a , continue to turn pages until there is no . The and in the returned fields are used for subsequent operations.
nextTokennextTokenworkspaceIdrootNodeId2. 查询知识库信息
2. Query knowledge base information
GET https://api.dingtalk.com/v2.0/wiki/workspaces/{workspaceId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>GET https://api.dingtalk.com/v2.0/wiki/workspaces/{workspaceId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>3. 查询目录结构(节点列表)
3. Query directory structure (node list)
用户想看知识库里有哪些文档/文件夹时:
GET https://api.dingtalk.com/v2.0/wiki/nodes?parentNodeId=<nodeId>&operatorId=<unionId>&maxResults=50
x-acs-dingtalk-access-token: <accessToken>parentNodeIdrootNodeIdnodeId每个节点包含:、、(/)、、、。
nodeIdnametypeFILEFOLDERcategoryworkspaceIdurlWhen the user wants to see what documents/folders are in the knowledge base:
GET https://api.dingtalk.com/v2.0/wiki/nodes?parentNodeId=<nodeId>&operatorId=<unionId>&maxResults=50
x-acs-dingtalk-access-token: <accessToken>Pass the of the knowledge base as to list the top-level content, pass the of a subfolder to view deeper content.
rootNodeIdparentNodeIdnodeIdEach node contains: , , (/), , , .
nodeIdnametypeFILEFOLDERcategoryworkspaceIdurl4. 查询单个节点信息(通过 nodeId)
4. Query single node information (by nodeId)
GET https://api.dingtalk.com/v2.0/wiki/nodes/{nodeId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>GET https://api.dingtalk.com/v2.0/wiki/nodes/{nodeId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>5. 通过文档链接查询节点信息
5. Query node information by document link
用户提供了文档 URL(如 )时:
https://alidocs.dingtalk.com/i/nodes/Xxx...POST https://api.dingtalk.com/v2.0/wiki/nodes/queryByUrl?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"url": "https://alidocs.dingtalk.com/i/nodes/<nodeId>",
"operatorId": "<unionId>"
}返回节点信息,其中 可作为后续内容读取的 。
nodeIddocKeyWhen the user provides a document URL (e.g. ):
https://alidocs.dingtalk.com/i/nodes/Xxx...POST https://api.dingtalk.com/v2.0/wiki/nodes/queryByUrl?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"url": "https://alidocs.dingtalk.com/i/nodes/<nodeId>",
"operatorId": "<unionId>"
}Return node information, where can be used as for subsequent content reading.
nodeIddocKey6. 创建文档
6. Create document
在指定知识库下新建文档:
POST https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"operatorId": "<unionId>",
"docType": "DOC",
"name": "<文档标题>"
}返回字段:
| 字段 | 说明 |
|---|---|
| 知识库节点 ID(用于删除) |
| 文档内容 Key(用于内容读写,≠ nodeId) |
| 实际所在知识库 ID(可能与请求的不同,删除时须使用此值) |
| 文档访问链接 |
固定填docType(ALIDOC 富文本格式)。"DOC"
Create a new document under the specified knowledge base:
POST https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"operatorId": "<unionId>",
"docType": "DOC",
"name": "<文档标题>"
}Return fields:
| Field | Description |
|---|---|
| Knowledge base node ID (used for deletion) |
| Document content Key (used for content reading and writing, ≠ nodeId) |
| Actual knowledge base ID (may be different from the requested one, must use this value when deleting) |
| Document access link |
is fixed asdocType(ALIDOC rich text format)."DOC"
10. 删除文档
10. Delete document
DELETE https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>workspaceIdnodeId200 {}DELETE https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>Both and are obtained from the response of creating the document. Success returns .
workspaceIdnodeId200 {}7. 读取文档正文内容(Block 结构)
7. Read document body content (Block structure)
用户想看文档里写了什么内容时,使用 读取文档 Block:
docKeyGET https://api.dingtalk.com/v1.0/doc/suites/documents/{docKey}/blocks?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>docKey- 通过 wiki nodes 接口查到的节点:(同一个值)
docKey = nodeId - 通过创建文档接口新建的文档:使用响应中的 字段(不是
docKey)nodeId
所需权限:
Storage.File.Read返回示例:
json
{
"result": {
"data": [
{ "blockType": "heading", "heading": { "level": "heading-2", "text": "快速开始" }, "index": 0, "id": "xxx" },
{ "blockType": "paragraph", "paragraph": { "text": "正文内容..." }, "index": 1, "id": "yyy" },
{ "blockType": "unknown", "index": 2, "id": "zzz" }
]
},
"success": true
}blockTypeheadingparagraphunorderedListorderedListtableblockquoteunknown将各 block 的文本提取后按 index 顺序拼接,即可重建文档文字内容。
即通过 wiki nodes 接口获取的docKey,是同一个值。nodeId
When the user wants to see the content of the document, use to read the document Block:
docKeyGET https://api.dingtalk.com/v1.0/doc/suites/documents/{docKey}/blocks?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>Source of :
docKey- Nodes queried through the wiki nodes interface: (same value)
docKey = nodeId - New documents created through the create document interface: use the field in the response (not
docKey)nodeId
Required permission:
Storage.File.ReadReturn example:
json
{
"result": {
"data": [
{ "blockType": "heading", "heading": { "level": "heading-2", "text": "快速开始" }, "index": 0, "id": "xxx" },
{ "blockType": "paragraph", "paragraph": { "text": "正文内容..." }, "index": 1, "id": "yyy" },
{ "blockType": "unknown", "index": 2, "id": "zzz" }
]
},
"success": true
}blockTypeheadingparagraphunorderedListorderedListtableblockquoteunknownExtract the text of each block and splice them in index order to reconstruct the document text content.
is thedocKeyobtained through the wiki nodes interface, they are the same value.nodeId
8. 写入/覆盖文档正文内容
8. Write/overwrite document body content
用户想修改文档内容时:
POST https://api.dingtalk.com/v1.0/doc/suites/documents/{docKey}/overwriteContent
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"operatorId": "<unionId>",
"docContent": "# 新标题\n\n新的正文内容,支持 Markdown 格式。",
"contentType": "markdown"
}⚠️ 写入操作会覆盖原有内容,执行前请与用户确认或先读取备份。
When the user wants to modify the document content:
POST https://api.dingtalk.com/v1.0/doc/suites/documents/{docKey}/overwriteContent
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"operatorId": "<unionId>",
"docContent": "# 新标题\n\n新的正文内容,支持 Markdown 格式。",
"contentType": "markdown"
}⚠️ The write operation will overwrite the original content, please confirm with the user or read and backup first before execution.
9. 文档成员管理
9. Document member management
添加文档成员:
POST https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}/members
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"operatorId": "<unionId>",
"members": [
{ "id": "<userId>", "roleType": "editor" }
]
}roleTypeviewereditorAdd document member:
POST https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}/members
x-acs-dingtalk-access-token: <accessToken>
Content-Type: application/json
{
"operatorId": "<unionId>",
"members": [
{ "id": "<userId>", "roleType": "editor" }
]
}roleTypeviewereditor典型场景
Typical Scenarios
"读取文档 X 的内容"
"Read the content of document X"
- 若用户提供了 URL,调用 获取
POST /v2.0/wiki/nodes/queryByUrlnodeId - 否则通过 遍历查找目标文档
GET /v2.0/wiki/nodes?parentNodeId=... - 用 作为
nodeId,调用docKeyGET /v1.0/doc/suites/documents/{docKey}/blocks - 将 block 文本按 index 顺序拼接后展示给用户
- If the user provides a URL, call to get
POST /v2.0/wiki/nodes/queryByUrlnodeId - Otherwise, traverse to find the target document through
GET /v2.0/wiki/nodes?parentNodeId=... - Use as
nodeId, calldocKeyGET /v1.0/doc/suites/documents/{docKey}/blocks - Splice the block text in index order and display it to the user
"把文档 X 的内容改成……"
"Change the content of document X to..."
- 先读取原内容,告知用户将被覆盖,请求确认
- 调用 ,传入新内容
POST /v1.0/doc/suites/documents/{docKey}/overwriteContent - 告知写入成功
- Read the original content first, inform the user that it will be overwritten, request confirmation
- Call , pass in the new content
POST /v1.0/doc/suites/documents/{docKey}/overwriteContent - Inform the user that the write is successful
"帮我在钉钉创建一个文档"
"Help me create a document in DingTalk"
- 询问放到哪个知识库(列出知识库或让用户说名称)
- 通过 找到对应
GET /v2.0/wiki/workspacesworkspaceId - 调用 ,
POST /v1.0/doc/workspaces/{workspaceId}/docsdocType: ALIDOC - 返回文档链接给用户
- Ask which knowledge base to put it in (list knowledge bases or let the user say the name)
- Find the corresponding through
workspaceIdGET /v2.0/wiki/workspaces - Call ,
POST /v1.0/doc/workspaces/{workspaceId}/docsdocType: ALIDOC - Return the document link to the user
"查看知识库 X 下有哪些文档"
"View the documents under knowledge base X"
- 通过 找到
GET /v2.0/wiki/workspaces和workspaceIdrootNodeId - 调用
GET /v2.0/wiki/nodes?parentNodeId={rootNodeId}&operatorId=... - 整理成目录树展示
- Find and
workspaceIdthroughrootNodeIdGET /v2.0/wiki/workspaces - Call
GET /v2.0/wiki/nodes?parentNodeId={rootNodeId}&operatorId=... - Organize into a directory tree for display
"把用户 xxx 加到文档 Y"
"Add user xxx to document Y"
- 确认文档的 和所在
nodeIdworkspaceId - 调用 ,指定
POST /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}/membersroleType
- Confirm the of the document and the
nodeIdit belongs toworkspaceId - Call , specify
POST /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}/membersroleType
错误处理
Error Handling
| HTTP 状态码 | 错误码 | 含义 | 处理方式 |
|---|---|---|---|
| 400 | | operatorId 未传 | 补充 operatorId(unionId) |
| 400 | | 参数类型错误 | operatorId 必须是 unionId,不是 userId |
| 401 | — | token 过期 | 重新获取 accessToken 后重试 |
| 403 | | 应用缺少权限 | 错误信息中有 |
| 404 | | 接口路径不存在 | 检查版本号(v1.0/v2.0)和路径是否正确 |
| 429 | — | 触发限流 | 等待 1 秒后重试 |
发生错误时,将响应体中的 和 展示给用户辅助排查。
codemessage| HTTP Status Code | Error Code | Meaning | Handling Method |
|---|---|---|---|
| 400 | | operatorId not passed | Supplement operatorId (unionId) |
| 400 | | Parameter type error | operatorId must be unionId, not userId |
| 401 | — | Token expired | Re-obtain accessToken and retry |
| 403 | | Application lacks permission | |
| 404 | | Interface path does not exist | Check whether the version number (v1.0/v2.0) and path are correct |
| 429 | — | Rate limit triggered | Wait 1 second and retry |
When an error occurs, display the and in the response body to the user to assist in troubleshooting.
codemessage所需应用权限
Required Application Permissions
| 功能 | 权限 scope |
|---|---|
| 查询知识库/节点 | |
| 读取文档正文 | |
| 写入文档正文 | |
| 创建/删除文档 | |
| 查询用户 unionId(获取 operatorId) | |
| Function | Permission scope |
|---|---|
| Query knowledge base/nodes | |
| Read document body | |
| Write document body | |
| Create/delete documents | |
| Query user unionId (obtain operatorId) | |