dingtalk-ai-table
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese钉钉 AI 表格技能
DingTalk AI Table Skill
负责钉钉 AI 表格( 格式多维表格)的所有操作,通过钉钉开放平台 Notable API 实现。
.able核心概念:
- AI 表格(文件):多维表格,使用 Notable API(
.able),不是普通电子表格/v1.0/notable - base_id:AI 表格文件的 nodeId,是表格在钉钉文档系统中的唯一标识
- 工作表(Sheet):AI 表格内的单张表,包含字段和记录
- 字段(Field):列定义,有名称和类型(、
text、number等)date - 记录(Record):数据行,包含各字段的值
API 详情见 。
references/api.mdResponsible for all operations of DingTalk AI Table (multi-dimensional table in format), implemented via the Notable API of DingTalk Open Platform.
.ableCore Concepts:
- AI Table (file): Multi-dimensional table, uses Notable API (
.able), is not a regular spreadsheet/v1.0/notable - base_id: nodeId of the AI Table file, the unique identifier of the table in DingTalk document system
- Worksheet (Sheet): A single table inside the AI Table, contains fields and records
- Field: Column definition, with name and type (,
text,number, etc.)date - Record: Data row, contains values of each field
For API details, see .
references/api.md配置管理(每次开始前必读)
Configuration Management (Must Read Before Each Start)
配置文件路径
Configuration File Path
~/.dingtalk-skills/config~/.dingtalk-skills/config本技能需要的配置说明
Configuration Instructions Required for This Skill
| 键 | 说明 | 来源 |
|---|---|---|
| 钉钉应用 appKey | 开放平台 → 应用管理 → 凭证信息 |
| 钉钉应用 appSecret | 同上 |
| 操作人 unionId | 见下方"为什么需要 operatorId"章节 |
| AI 表格的 nodeId | 从 AI 表格分享链接提取 |
| 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 |
| nodeId of AI Table | Extracted from AI Table share link |
启动流程(每次执行任务前)
Startup Process (Before Each Task Execution)
- 读取配置:检查 是否存在,解析已有键值
~/.dingtalk-skills/config - 识别缺失项:找出上表中尚未配置的键
- 一次性收集:将所有缺失项合并为一条提问,不要逐条询问,例如:
需要以下信息才能继续(已有的无需再填):
- 钉钉应用 appKey(钉钉开放平台 → 应用管理 → 凭证信息)
- 钉钉应用 appSecret
- AI 表格链接(用于提取 base_id)
- 你的钉钉 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
- AI Table link (used to extract base_id)
- Your DingTalk userId or unionId (to operate the table as your identity)
- Persistence: Append the values provided by the user to the config, read directly later without asking again
- Execute task: Start operation after 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>" }所有请求需携带:
- 请求头:
x-acs-dingtalk-access-token: <accessToken> - 查询参数:(所有写操作及部分读操作必须)
operatorId=<用户 unionId>
Before each API call, use appKey/appSecret to get the current accessToken (valid for 2 hours):
POST https://api.dingtalk.com/v1.0/oauth2/accessToken
Content-Type: application/json
{ "appKey": "<应用 appKey>", "appSecret": "<应用 appSecret>" }All requests need to carry:
- Request header:
x-acs-dingtalk-access-token: <accessToken> - Query parameter: (required for all write operations and some read operations)
operatorId=<user unionId>
为什么需要 base_id
Why base_id is required
钉钉文档系统中每个文件(文档、表格、AI 表格)都有一个全局唯一的 nodeId,即 。它的作用类似数据库主键——API 通过它定位到具体是哪个 AI 表格文件,因为账号下可能有多个 文件。
base_id.able从链接提取 base_id:
https://alidocs.dingtalk.com/i/nodes/<base_id>?...
↑ 这一段就是 base_id请用户提供 AI 表格的分享链接,从 后截取 ID 片段。首次获取后写入 config,后续无需再问。
/nodes/Each file (document, spreadsheet, AI Table) in the DingTalk document system has a globally unique nodeId, which is . Its function is similar to a database primary key - the API locates the specific AI Table file through it, because there may be multiple files under the account.
base_id.ableExtract base_id from link:
https://alidocs.dingtalk.com/i/nodes/<base_id>?...
↑ This part is base_idAsk the user to provide the share link of the AI Table, and intercept the ID fragment after . Write it to the config after the first acquisition, no need to ask again later.
/nodes/为什么需要 operatorId(unionId)
Why operatorId (unionId) is required
钉钉开放平台要求所有写操作必须代表一个真实用户身份执行,而不是以匿名应用身份操作。 就是声明"这个操作是谁做的"——它会被记录到变更日志、触发对应用户的通知,并用于权限校验。
operatorId- 值为操作人的 (钉钉跨组织唯一 ID),不是
unionId(仅组织内唯一)userId - 错误传入 userId 会导致权限报错或操作归属错误
DingTalk Open Platform requires all write operations to be performed on behalf of a real user identity, not an anonymous application identity. is to declare "who performed this operation" - it will be recorded in the change log, trigger the corresponding user's notification, and be 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 incorrect operation attribution
如何获取 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: Get the old (different from the new version of 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 and get 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 unionId.
result.unionidNote:(with underscore) may be empty in exclusive DingTalk organizations, please useresult.union_id.result.unionid
Method 2: Bot/message scenario
When a user triggers via DingTalk bot or message, the message body directly contains the field, which can be used directly 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 it to the config after acquisition, no need to obtain it repeatedly for subsequent operations.
核心操作
Core Operations
1. 列出工作表
1. List worksheets
GET https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets?operatorId={operatorId}
x-acs-dingtalk-access-token: <accessToken>返回:
json
{
"value": [
{ "id": "HAcL4SD", "name": "项目" },
{ "id": "nr2iEiW", "name": "任务" }
]
}GET https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets?operatorId={operatorId}
x-acs-dingtalk-access-token: <accessToken>Return:
json
{
"value": [
{ "id": "HAcL4SD", "name": "项目" },
{ "id": "nr2iEiW", "name": "任务" }
]
}2. 查询单个工作表
2. Query single worksheet
GET https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}?operatorId={operatorId}返回:
{ "id": "HAcL4SD", "name": "项目" }GET https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}?operatorId={operatorId}Return:
{ "id": "HAcL4SD", "name": "项目" }3. 新建工作表
3. Create new worksheet
POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets?operatorId={operatorId}
Content-Type: application/json
{
"name": "新工作表名称",
"fields": [
{ "name": "标题", "type": "text" },
{ "name": "数量", "type": "number" }
]
}fields返回:
{ "id": "新sheetId", "name": "新工作表名称" }POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets?operatorId={operatorId}
Content-Type: application/json
{
"name": "新工作表名称",
"fields": [
{ "name": "标题", "type": "text" },
{ "name": "数量", "type": "number" }
]
}fieldsReturn:
{ "id": "新sheetId", "name": "新工作表名称" }4. 删除工作表
4. Delete worksheet
DELETE https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}?operatorId={operatorId}返回:
⚠️ 不可恢复,执行前需用户确认。
{ "success": true }⚠️ 不可恢复,执行前需用户确认。
DELETE https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}?operatorId={operatorId}Return:
⚠️ Unrecoverable, user confirmation is required before execution.
{ "success": true }⚠️ Unrecoverable, user confirmation is required before execution.
5. 列出字段
5. List fields
GET https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields?operatorId={operatorId}返回:
json
{
"value": [
{ "id": "6mNRNHb", "name": "标题", "type": "text" },
{ "id": "BDGLCo2", "name": "截止日期", "type": "date", "property": { "formatter": "YYYY-MM-DD" } },
{ "id": "mr8APlG", "name": "数量", "type": "number", "property": { "formatter": "INT" } }
]
}GET https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields?operatorId={operatorId}Return:
json
{
"value": [
{ "id": "6mNRNHb", "name": "标题", "type": "text" },
{ "id": "BDGLCo2", "name": "截止日期", "type": "date", "property": { "formatter": "YYYY-MM-DD" } },
{ "id": "mr8APlG", "name": "数量", "type": "number", "property": { "formatter": "INT" } }
]
}6. 新建字段
6. Create new field
POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields?operatorId={operatorId}
Content-Type: application/json
{
"name": "字段名称",
"type": "number"
}typetextnumberdate返回:
{ "id": "新fieldId", "name": "字段名称", "type": "number", "property": { "formatter": "INT" } }POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields?operatorId={operatorId}
Content-Type: application/json
{
"name": "字段名称",
"type": "number"
}Common values for : (text), (number), (date)
Return:
typetextnumberdateReturn:
{ "id": "新fieldId", "name": "字段名称", "type": "number", "property": { "formatter": "INT" } }7. 更新字段
7. Update field
PUT https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields/{field_id}?operatorId={operatorId}
Content-Type: application/json
{
"name": "新字段名称"
}返回:(通过重新查询列表确认名称已变更)
{ "id": "fieldId" }PUT https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields/{field_id}?operatorId={operatorId}
Content-Type: application/json
{
"name": "新字段名称"
}Return: (confirm the name has been changed by re-querying the list)
{ "id": "fieldId" }8. 删除字段
8. Delete field
DELETE https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields/{field_id}?operatorId={operatorId}返回:
⚠️ 删除字段会同时删除该列所有数据,执行前需用户确认。
{ "success": true }⚠️ 删除字段会同时删除该列所有数据,执行前需用户确认。
DELETE https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/fields/{field_id}?operatorId={operatorId}Return:
⚠️ Deleting a field will delete all data in this column at the same time, user confirmation is required before execution.
{ "success": true }⚠️ Deleting a field will delete all data in this column at the same time, user confirmation is required before execution.
9. 新增记录(最常用操作)
9. Add records (most commonly used operation)
POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records?operatorId={operatorId}
Content-Type: application/json
{
"records": [
{ "fields": { "标题": "任务一", "数量": 3 } },
{ "fields": { "标题": "任务二", "数量": 5 } }
]
}fields返回:
{ "value": [{ "id": "记录ID1" }, { "id": "记录ID2" }] }POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records?operatorId={operatorId}
Content-Type: application/json
{
"records": [
{ "fields": { "标题": "任务一", "数量": 3 } },
{ "fields": { "标题": "任务二", "数量": 5 } }
]
}Use field name (not ID) as the key in .
Return:
fieldsReturn:
{ "value": [{ "id": "记录ID1" }, { "id": "记录ID2" }] }10. 查询记录列表
10. Query record list
POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records/list?operatorId={operatorId}
Content-Type: application/json
{
"maxResults": 20,
"nextToken": ""
}返回:
json
{
"records": [
{
"id": "RNXU1Vm2L2",
"fields": { "标题": "任务一", "数量": 3 },
"createdTime": 1772723541439,
"createdBy": { "unionId": "xxx" },
"lastModifiedTime": 1772723541439,
"lastModifiedBy": { "unionId": "xxx" }
}
],
"hasMore": false,
"nextToken": ""
}翻页:上次响应 时,将 传入下次请求。
hasMore=truenextTokenPOST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records/list?operatorId={operatorId}
Content-Type: application/json
{
"maxResults": 20,
"nextToken": ""
}Return:
json
{
"records": [
{
"id": "RNXU1Vm2L2",
"fields": { "标题": "任务一", "数量": 3 },
"createdTime": 1772723541439,
"createdBy": { "unionId": "xxx" },
"lastModifiedTime": 1772723541439,
"lastModifiedBy": { "unionId": "xxx" }
}
],
"hasMore": false,
"nextToken": ""
}Pagination: When the last response , pass to the next request.
hasMore=truenextToken11. 更新记录
11. Update record
PUT https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records?operatorId={operatorId}
Content-Type: application/json
{
"records": [
{ "id": "记录ID", "fields": { "标题": "新标题" } }
]
}返回:
只传需要修改的字段,未传字段保持不变。
{ "value": [{ "id": "记录ID" }] }只传需要修改的字段,未传字段保持不变。
PUT https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records?operatorId={operatorId}
Content-Type: application/json
{
"records": [
{ "id": "记录ID", "fields": { "标题": "新标题" } }
]
}Return:
Only pass the fields that need to be modified, the fields not passed remain unchanged.
{ "value": [{ "id": "记录ID" }] }Only pass the fields that need to be modified, the fields not passed remain unchanged.
12. 删除记录
12. Delete record
POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records/delete?operatorId={operatorId}
Content-Type: application/json
{
"recordIds": ["记录ID1", "记录ID2"]
}返回:
{ "success": true }POST https://api.dingtalk.com/v1.0/notable/bases/{base_id}/sheets/{sheet_id}/records/delete?operatorId={operatorId}
Content-Type: application/json
{
"recordIds": ["记录ID1", "记录ID2"]
}Return:
{ "success": true }典型场景
Typical Scenarios
"查看 AI 表格里有哪些工作表"
"Check what worksheets are in the AI Table"
- 询问 AI 表格链接,提取 base_id
- 获取工作表列表
GET /sheets - 展示工作表名称和 ID
- Ask for the AI Table link, extract base_id
- to get the worksheet list
GET /sheets - Display worksheet names and IDs
"往 AI 表格的'任务'工作表添加几条记录"
"Add several records to the 'Task' worksheet of the AI Table"
- 找到目标工作表的 sheet_id
GET /sheets - 了解现有字段名称和类型
GET /fields - 批量插入,fields 中用字段名称作键
POST /records - 告知写入成功及记录 ID
- to find the sheet_id of the target worksheet
GET /sheets - to understand the existing field names and types
GET /fields - to insert in batches, use field names as keys in fields
POST /records - Inform that the writing is successful and the record IDs
"查询'任务'表中所有记录"
"Query all records in the 'Task' table"
- 找到 sheet_id
GET /sheets - 翻页获取所有记录
POST /records/list - 整理为表格形式展示
- to find sheet_id
GET /sheets - to get all records by pagination
POST /records/list - Organize into table form for display
"删除某条记录"
"Delete a record"
- 先 定位目标记录(让用户确认)
POST /records/list - 传入 recordId
POST /records/delete - 告知删除成功
- First to locate the target record (let user confirm)
POST /records/list - to pass recordId
POST /records/delete - Inform that the deletion is successful
"给工作表新增一个'备注'文本字段"
"Add a 'Remark' text field to the worksheet"
- 找到 sheet_id
GET /sheets - ,
POST /fields{ "name": "备注", "type": "text" } - 返回新字段 ID 并告知成功
- to find sheet_id
GET /sheets - ,
POST /fields{ "name": "备注", "type": "text" } - Return the new field ID and inform success
字段类型参考
Field Type Reference
| type | 含义 |
|---|---|
| 纯文本 |
| 数字(含 |
| 日期(含 |
| type | Meaning |
|---|---|
| Plain text |
| Number (including |
| Date (including |
错误处理
Error Handling
| HTTP 状态码 / code | 含义 | 处理方式 |
|---|---|---|
| 401 | token 过期 | 重新获取 accessToken |
| 403 | 权限不足 | 检查应用是否开通 Notable 相关权限 |
| base_id 无效或无权访问 | 确认 AI 表格 nodeId 正确且已授权 |
| 404 | 工作表或字段不存在 | 确认 sheet_id / field_id 正确 |
| 429 | 触发限流 | 等待 1 秒后重试 |
| HTTP Status Code / code | Meaning | Handling Method |
|---|---|---|
| 401 | Token expired | Re-acquire accessToken |
| 403 | Insufficient permissions | Check if the application has enabled Notable related permissions |
| Invalid base_id or no access permission | Confirm that the AI Table nodeId is correct and authorized |
| 404 | Worksheet or field does not exist | Confirm that sheet_id / field_id is correct |
| 429 | Rate limit triggered | Retry after waiting for 1 second |