dingtalk-document

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

钉钉文档技能

DingTalk Docs Skill

负责钉钉知识库和文档的所有操作,通过钉钉开放平台 API 实现。
API 详情见
references/api.md

Responsible 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 共用同一文件)
~/.dingtalk-skills/config
(retained across sessions, shared by all dingtalk-skills)

本技能需要的配置

Required Configurations for This Skill

说明来源
DINGTALK_APP_KEY
钉钉应用 appKey开放平台 → 应用管理 → 凭证信息
DINGTALK_APP_SECRET
钉钉应用 appSecret同上
DINGTALK_OPERATOR_ID
操作人 unionId见下方"为什么需要 operatorId"章节
KeyDescriptionSource
DINGTALK_APP_KEY
DingTalk application appKeyOpen Platform → Application Management → Credential Information
DINGTALK_APP_SECRET
DingTalk application appSecretSame as above
DINGTALK_OPERATOR_ID
Operator unionIdSee the "Why operatorId is required" section below

启动流程(每次执行任务前)

Startup Process (Before Each Task Execution)

  1. 读取配置:检查
    ~/.dingtalk-skills/config
    是否存在,解析已有键值
  2. 识别缺失项:找出上表中尚未配置的键
  3. 一次性收集:将所有缺失项合并为一条提问,不要逐条询问,例如:
    需要以下信息才能继续(已有的无需再填):
    • 钉钉应用 appKey(钉钉开放平台 → 应用管理 → 凭证信息)
    • 钉钉应用 appSecret
    • 你的钉钉 userId 或 unionId(以便以你的身份操作文档)
  4. 持久化:将用户提供的值追加写入 config,后续直接读取,无需再问
  5. 执行任务:配置完整后开始操作
注意
APP_KEY
/
APP_SECRET
/
OPERATOR_ID
属于凭证,禁止在输出中完整打印,确认时仅显示前 4 位 +
****

  1. Read configuration: Check if
    ~/.dingtalk-skills/config
    exists, parse existing key-value pairs
  2. Identify missing items: Find the keys in the above table that have not been configured yet
  3. 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)
  4. Persistence: Append the values provided by the user to the config file, read directly in the future without asking again
  5. Execute task: Start operation after the configuration is complete
Note:
APP_KEY
/
APP_SECRET
/
OPERATOR_ID
are credentials, it is forbidden to print them completely in the output, only show the first 4 digits +
****
when 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
就是声明"这个操作是谁做的"——它会被记录到文档的变更历史,并用于权限校验。
  • 值为操作人的
    unionId
    (钉钉跨组织唯一 ID),不是
    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.
operatorId
is to declare "who performed this operation" - it will be recorded in the document change history and used for permission verification.
  • The value is the operator's
    unionId
    (DingTalk cross-organization unique ID), not
    userId
    (only unique within the organization)
  • Incorrectly passing userId will cause permission errors or document ownership errors

如何获取 unionId

How to get unionId

方法一:已知 userId → 换取 unionId(最常用)
userId
通常可从通讯录、免登、消息回调等场景直接获得。
第一步:获取旧式
access_token
(与新版 accessToken 不同,此处单独获取):
GET 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>" }
返回字段
result.unionid
(无下划线)即为 unionId。
注意:
result.union_id
(有下划线)在专属钉钉组织中可能为空,请使用
result.unionid
方法二:机器人/消息场景
用户通过钉钉机器人或消息触发时,消息体中直接包含
senderUnionId
字段,可直接作为
operatorId
使用,无需额外查询。
方法三:直接询问用户
若上述方式不可用,在初始配置阶段询问用户提供 userId 或 unionId(均可在钉钉个人信息页查看)。获取后写入 config,后续操作无需重复获取。

Method 1: Known userId → exchange for unionId (most commonly used)
userId
can usually be obtained directly from address book, free login, message callback and other scenarios.
Step 1: Obtain the old version
access_token
(different from the new version accessToken, obtained separately here):
GET 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
result.unionid
(no underscore) is the unionId.
Note:
result.union_id
(with underscore) may be empty in exclusive DingTalk organizations, please use
result.unionid
.
Method 2: Robot/message scenario
When a user triggers through a DingTalk robot or message, the message body directly contains the
senderUnionId
field, which can be directly used as
operatorId
without additional query.
Method 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>
如有
nextToken
则继续翻页,直到无
nextToken
为止。返回字段中
workspaceId
rootNodeId
供后续操作使用。

When 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
nextToken
, continue to turn pages until there is no
nextToken
. The
workspaceId
and
rootNodeId
in the returned fields are used for subsequent operations.

2. 查询知识库信息

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>
parentNodeId
传知识库的
rootNodeId
可列出顶层内容,传子文件夹
nodeId
可深入查看。
每个节点包含:
nodeId
name
type
FILE
/
FOLDER
)、
category
workspaceId
url

When 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
rootNodeId
of the knowledge base as
parentNodeId
to list the top-level content, pass the
nodeId
of a subfolder to view deeper content.
Each node contains:
nodeId
,
name
,
type
(
FILE
/
FOLDER
),
category
,
workspaceId
,
url
.

4. 查询单个节点信息(通过 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>"
}
返回节点信息,其中
nodeId
可作为后续内容读取的
docKey

When 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
nodeId
can be used as
docKey
for subsequent content reading.

6. 创建文档

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": "<文档标题>"
}
返回字段:
字段说明
nodeId
知识库节点 ID(用于删除)
docKey
文档内容 Key(用于内容读写,≠ nodeId)
workspaceId
实际所在知识库 ID(可能与请求的不同,删除时须使用此值)
url
文档访问链接
docType
固定填
"DOC"
(ALIDOC 富文本格式)。

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:
FieldDescription
nodeId
Knowledge base node ID (used for deletion)
docKey
Document content Key (used for content reading and writing, ≠ nodeId)
workspaceId
Actual knowledge base ID (may be different from the requested one, must use this value when deleting)
url
Document access link
docType
is fixed as
"DOC"
(ALIDOC rich text format).

10. 删除文档

10. Delete document

DELETE https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>
workspaceId
nodeId
均从创建文档的响应中获取。成功返回
200 {}

DELETE https://api.dingtalk.com/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}?operatorId=<unionId>
x-acs-dingtalk-access-token: <accessToken>
Both
workspaceId
and
nodeId
are obtained from the response of creating the document. Success returns
200 {}
.

7. 读取文档正文内容(Block 结构)

7. Read document body content (Block structure)

用户想看文档里写了什么内容时,使用
docKey
读取文档 Block:
GET 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
}
blockType
枚举:
heading
paragraph
unorderedList
orderedList
table
blockquote
unknown
(代码块/图片等富文本暂未解析)。
将各 block 的文本提取后按 index 顺序拼接,即可重建文档文字内容。
docKey
即通过 wiki nodes 接口获取的
nodeId
,是同一个值。

When the user wants to see the content of the document, use
docKey
to read the document Block:
GET 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:
    docKey = nodeId
    (same value)
  • New documents created through the create document interface: use the
    docKey
    field in the response (not
    nodeId
    )
Required permission:
Storage.File.Read
Return 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
}
blockType
enumeration:
heading
,
paragraph
,
unorderedList
,
orderedList
,
table
,
blockquote
,
unknown
(rich text such as code blocks/images are not parsed yet).
Extract the text of each block and splice them in index order to reconstruct the document text content.
docKey
is the
nodeId
obtained through the wiki nodes interface, they are the same value.

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" }
  ]
}
roleType
可选:
viewer
(只读)、
editor
(可编辑)

Add 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" }
  ]
}
roleType
options:
viewer
(read-only),
editor
(editable)

典型场景

Typical Scenarios

"读取文档 X 的内容"

"Read the content of document X"

  1. 若用户提供了 URL,调用
    POST /v2.0/wiki/nodes/queryByUrl
    获取
    nodeId
  2. 否则通过
    GET /v2.0/wiki/nodes?parentNodeId=...
    遍历查找目标文档
  3. nodeId
    作为
    docKey
    ,调用
    GET /v1.0/doc/suites/documents/{docKey}/blocks
  4. 将 block 文本按 index 顺序拼接后展示给用户
  1. If the user provides a URL, call
    POST /v2.0/wiki/nodes/queryByUrl
    to get
    nodeId
  2. Otherwise, traverse to find the target document through
    GET /v2.0/wiki/nodes?parentNodeId=...
  3. Use
    nodeId
    as
    docKey
    , call
    GET /v1.0/doc/suites/documents/{docKey}/blocks
  4. Splice the block text in index order and display it to the user

"把文档 X 的内容改成……"

"Change the content of document X to..."

  1. 先读取原内容,告知用户将被覆盖,请求确认
  2. 调用
    POST /v1.0/doc/suites/documents/{docKey}/overwriteContent
    ,传入新内容
  3. 告知写入成功
  1. Read the original content first, inform the user that it will be overwritten, request confirmation
  2. Call
    POST /v1.0/doc/suites/documents/{docKey}/overwriteContent
    , pass in the new content
  3. Inform the user that the write is successful

"帮我在钉钉创建一个文档"

"Help me create a document in DingTalk"

  1. 询问放到哪个知识库(列出知识库或让用户说名称)
  2. 通过
    GET /v2.0/wiki/workspaces
    找到对应
    workspaceId
  3. 调用
    POST /v1.0/doc/workspaces/{workspaceId}/docs
    docType: ALIDOC
  4. 返回文档链接给用户
  1. Ask which knowledge base to put it in (list knowledge bases or let the user say the name)
  2. Find the corresponding
    workspaceId
    through
    GET /v2.0/wiki/workspaces
  3. Call
    POST /v1.0/doc/workspaces/{workspaceId}/docs
    ,
    docType: ALIDOC
  4. Return the document link to the user

"查看知识库 X 下有哪些文档"

"View the documents under knowledge base X"

  1. 通过
    GET /v2.0/wiki/workspaces
    找到
    workspaceId
    rootNodeId
  2. 调用
    GET /v2.0/wiki/nodes?parentNodeId={rootNodeId}&operatorId=...
  3. 整理成目录树展示
  1. Find
    workspaceId
    and
    rootNodeId
    through
    GET /v2.0/wiki/workspaces
  2. Call
    GET /v2.0/wiki/nodes?parentNodeId={rootNodeId}&operatorId=...
  3. Organize into a directory tree for display

"把用户 xxx 加到文档 Y"

"Add user xxx to document Y"

  1. 确认文档的
    nodeId
    和所在
    workspaceId
  2. 调用
    POST /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}/members
    ,指定
    roleType

  1. Confirm the
    nodeId
    of the document and the
    workspaceId
    it belongs to
  2. Call
    POST /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}/members
    , specify
    roleType

错误处理

Error Handling

HTTP 状态码错误码含义处理方式
400
MissingoperatorId
operatorId 未传补充 operatorId(unionId)
400
paramError
参数类型错误operatorId 必须是 unionId,不是 userId
401token 过期重新获取 accessToken 后重试
403
Forbidden.AccessDenied.AccessTokenPermissionDenied
应用缺少权限错误信息中有
requiredScopes
,提示用户在开放平台开通对应权限
404
InvalidAction.NotFound
接口路径不存在检查版本号(v1.0/v2.0)和路径是否正确
429触发限流等待 1 秒后重试
发生错误时,将响应体中的
code
message
展示给用户辅助排查。
HTTP Status CodeError CodeMeaningHandling Method
400
MissingoperatorId
operatorId not passedSupplement operatorId (unionId)
400
paramError
Parameter type erroroperatorId must be unionId, not userId
401Token expiredRe-obtain accessToken and retry
403
Forbidden.AccessDenied.AccessTokenPermissionDenied
Application lacks permission
requiredScopes
is included in the error message, prompt the user to activate the corresponding permission on the open platform
404
InvalidAction.NotFound
Interface path does not existCheck whether the version number (v1.0/v2.0) and path are correct
429Rate limit triggeredWait 1 second and retry
When an error occurs, display the
code
and
message
in the response body to the user to assist in troubleshooting.

所需应用权限

Required Application Permissions

功能权限 scope
查询知识库/节点
Wiki.Node.Read
读取文档正文
Storage.File.Read
写入文档正文
Storage.File.Write
创建/删除文档
Storage.File.Write
查询用户 unionId(获取 operatorId)
Contact.User.Read
FunctionPermission scope
Query knowledge base/nodes
Wiki.Node.Read
Read document body
Storage.File.Read
Write document body
Storage.File.Write
Create/delete documents
Storage.File.Write
Query user unionId (obtain operatorId)
Contact.User.Read