Skill: andylikescodes/dingtalk-csa (version 1.0.2)

--- name: dingtalk-csa description: 钉盘助手 (DingTalk Cloud Storage Assistant) - 管理钉钉云盘空间、文件和文档。用当用户要求读写钉盘文件、管理团队空间、上传下载文档、操作adoc文档时触发。也适用于钉钉文件分析、报告生成、团队协作等场景。触发词：钉盘、钉钉云盘、DingTalk storage、钉钉文件、钉钉文档。 metadata: openclaw: requires: env: DINGTALK_APP_KEY: "钉钉应用 AppKey" DINGTALK_APP_SECRET: "钉钉应用 AppSecret（敏感凭据，切勿明文写在配置文件中）" --- # 钉盘助手 DingTalk Cloud Storage Assistant 管理钉钉云盘：团队空间、文件上传下载、钉钉文档读写、团队协作。 ## 🔒 安全模型：读任意，写受限 **核心原则：AI 可以读取任何可访问的空间，但只能写入指定的文件夹。** 这确保了： - ✅ AI 可以读取团队共享文件（用于分析、总结等） - ✅ AI 只能在预批准的协作文件夹中写入 - ❌ 重要文件不会被意外修改或覆盖 ### 配置写入白名单 在 SKILL.md 的 `ALLOWED_WRITE_PATHS` 中配置允许写入的位置： ```yaml # === 写入白名单 === # AI 只能写入以下位置 ALLOWED_WRITE_PATHS: - spaceId: "YOUR_SPACE_ID" parentDentryId: "YOUR_FOLDER_ID" path: "/AI_Collab" description: "AI协作文件夹 - AI只允许写入此文件夹" ``` **执行规则：** 1. **读取**：允许访问任何空间/文件夹 ✅ 2. **写入**（上传文件、创建文档、创建文件夹）：仅在白名单内 ✅ 3. **删除**：不允许 ❌ 4. 任何写入操作前，必须先检查目标路径是否在白名单中 ## 🔑 认证 所有 API 调用需要 access token，通过以下方式获取： ```bash curl -X POST 'https://api.dingtalk.com/v1.0/oauth2/accessToken' \ -H 'Content-Type: application/json' \ -d '{"appKey": "'"$DINGTALK_APP_KEY"'", "appSecret": "'"$DINGTALK_APP_SECRET"'"}' ``` **环境变量配置：** - `DINGTALK_APP_KEY` — 钉钉应用的 AppKey - `DINGTALK_APP_SECRET` — 钉钉应用的 AppSecret（⚠️ 绝不要明文写在代码或配置文件中） Token 有效期 2 小时，请缓存避免重复请求。 ## 核心操作 ### 1. 列出空间（读 ✅） ```bash # 企业空间 curl -X GET "https://api.dingtalk.com/v1.0/drive/spaces?unionId=<unionId>&spaceType=org&maxResults=50" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" ``` ### 2. 列出文件（读 ✅） ```bash curl -X POST "https://api.dingtalk.com/v1.0/storage/spaces/{spaceId}/dentries/listAll" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \ -H 'Content-Type: application/json' \ -d '{"unionId": "<unionId>"}' ``` ### 3. 读取文档内容（读 ✅） .adoc 格式的钉钉文档，用 `uuid` 作为 documentId： ```bash curl -X GET "https://api.dingtalk.com/v1.0/doc/suites/documents/{documentId}/blocks?operatorId=<unionId>" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" ``` ### 4. 写入文档内容（写 ⚠️ 检查白名单） ```bash # 覆写整个文档（Markdown格式） curl -X POST "https://api.dingtalk.com/v1.0/doc/suites/documents/{documentId}/overwriteContent?operatorId=<unionId>" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \ -H 'Content-Type: application/json' \ -d '{"content": "# Markdown Content", "dataType": "markdown"}' # 追加内容到文档 curl -X POST "https://api.dingtalk.com/v1.0/doc/suites/documents/{documentId}/content?operatorId=<unionId>" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \ -H 'Content-Type: application/json' \ -d '{"content": {"type": "markdown", "content": "## New Section"}}' ``` ### 5. 创建文件夹（写 ⚠️ 检查白名单） ```bash curl -X POST "https://api.dingtalk.com/v1.0/storage/spaces/{spaceId}/dentries/{parentId}/folders" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \ -H 'Content-Type: application/json' \ -d '{"unionId": "<unionId>", "name": "New Folder"}' ``` ### 6. 上传文件（写 ⚠️ 检查白名单） 3步流程，详见 [references/upload-guide.md](references/upload-guide.md) ### 7. 下载文件（读 ✅） ```bash curl -X POST "https://api.dingtalk.com/v1.0/storage/spaces/{spaceId}/dentries/{dentryId}/downloadInfos/query?unionId=<unionId>" \ -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \ -H 'Content-Type: application/json' \ -d '{"option": {"version": 1}}' ``` ## 必需权限 详见 [references/permissions.md](references/permissions.md) 核心权限（7个）： 1. `Storage.Space.Read` - 钉盘应用盘空间读 2. `Storage.Space.Write` - 钉盘应用盘空间写 3. `Storage.File.Read` - 钉盘应用文件读 4. `Storage.File.Write` - 钉盘应用文件写 5. `Storage.UploadInfo.Read` - 钉盘上传信息读 6. `Storage.DownloadInfo.Read` - 钉盘下载信息读 7. `企业存储文件下载信息读权限` - 企业存储文件下载（配合#6） ## 参考文档 - [permissions.md](references/permissions.md) - 完整权限清单和开通指南 - [permission-list-share.md](references/permission-list-share.md) - 可分享给管理员的权限清单 - [upload-guide.md](references/upload-guide.md) - 3步上传流程详解

path: PERMISSION_GUIDE.md
content: # 钉盘助手 (dingtalk-csa) 权限开通指南

## 📖 适用对象

本文档适用于想要在企业内部部署 **钉盘助手 (dingtalk-csa)** 技能的管理员或开发者。

钉盘助手是一个通用的钉钉云存储管理技能，支持：
- 钉盘空间管理（列出、创建、查询）
- 文件操作（上传、下载、列出）
- 钉钉文档读写（adoc 格式）
- 通讯录查询（部门、用户）

---

## 🔑 必需开通的权限

在钉钉开放平台为你的应用开通以下权限后，钉盘助手才能正常工作。

### ✅ 核心权限（必须全部开通）

| # | 权限名称 | 权限标识 | 用途 | 缺少后果 |
|---|---------|---------|------|---------|
| 1 | **钉盘应用盘空间读权限** | `Storage.Space.Read` | 列出和查看钉盘空间 | ❌ 无法列出空间列表 |
| 2 | **钉盘应用盘空间写权限** | `Storage.Space.Write` | 创建新空间 | ❌ 无法创建空间 |
| 3 | **钉盘应用文件读权限** | `Storage.File.Read` | 列出和查询文件信息 | ❌ 无法查看文件列表 |
| 4 | **钉盘应用文件写权限** | `Storage.File.Write` | 上传文件、写入文档内容 | ❌ 无法上传文件或写入报告 |
| 5 | **钉盘上传信息读权限** | `Storage.UploadInfo.Read` | 获取文件上传凭证（OSS 地址） | ❌ 无法获取上传地址 |
| 6 | **钉盘下载信息读权限** | `Storage.DownloadInfo.Read` | 获取文件下载信息 | ❌ 无法下载文件 |
| 7 | **企业存储文件下载信息读权限** | （新版 API 权限） | 配合 #6 完成文件下载 | ❌ 下载链接无法获取 |

### 🟡 可选权限（按需开通）

| # | 权限名称 | 权限标识 | 用途 | 建议场景 |
|---|---------|---------|------|---------|
| 8 | **通讯录用户详情读权限** | `qyapi_get_member` | 获取用户手机号、邮箱等 | 需要查询同事联系方式 |
| 9 | **通讯录部门列表读权限** | `qyapi_get_department_list` | 获取组织架构部门列表 | 需要按部门管理文件权限 |
| 10 | **个人用户信息读权限** | `Contact.User.Read` | 新版 API 查询用户信息 | 使用新版通讯录 API |
| 11 | **企业钉盘基础权限** | `qyapi_cspace_base` | 旧版自定义空间 API | 兼容旧版系统（可选） |

---

## 📝 开通步骤

### 步骤 1：登录钉钉开放平台

访问：https://open-dev.dingtalk.com/

使用企业管理员账号登录。

### 步骤 2：进入应用管理

1. 点击顶部导航栏 **应用开发**
2. 找到你的企业内部应用（或创建一个新应用）
3. 点击进入应用详情页

### 步骤 3：申请权限

1. 在左侧菜单点击 **权限管理**
2. 在权限列表页面，使用搜索框搜索权限名称
3. 逐个搜索并申请上述 **核心权限**（#1-#7）

**搜索关键词建议：**
- 搜索 "空间读" → 找到 "钉盘应用盘空间读权限"
- 搜索 "空间写" → 找到 "钉盘应用盘空间写权限"
- 搜索 "文件读" → 找到 "钉盘应用文件读权限"
- 搜索 "文件写" → 找到 "钉盘应用文件写权限"
- 搜索 "上传" → 找到 "钉盘上传信息读权限"
- 搜索 "下载" → 找到 "钉盘下载信息读权限" 和 "企业存储文件下载信息读权限"

### 步骤 4：提交申请

1. 点击权限右侧的 **申请** 按钮
2. 填写申请理由（示例："用于企业内部文件管理和自动化报告生成"）
3. 提交申请

### 步骤 5：等待审批

- 企业内部应用的权限申请需要 **企业管理员审批**
- 审批通过后，权限通常 **5-10 分钟** 内生效

### 步骤 6：验证权限

权限生效后，可以运行以下测试命令验证：

```bash
# 替换为你的 AppKey 和 AppSecret
APP_KEY="your_app_key"
APP_SECRET="your_app_secret"

# 1. 获取 access_token
[REDACTED_SECRET] -s -X POST 'https://api.dingtalk.com/v1.0/oauth2/accessToken' \
  -H 'Content-Type: application/json' \
  -d "{\"appKey\":\"$APP_KEY\",\"appSecret\":\"$APP_SECRET\"}" | \
  python3 -c "import json,sys; print(json.load(sys.stdin)['accessToken'])")

echo "[REDACTED_SECRET]"

# 2. 测试空间列表（验证 Storage.Space.Read）
echo "=== 测试空间列表 ==="
curl -s -X GET "https://api.dingtalk.com/v1.0/drive/spaces?unionId=YOUR_UNION_ID&spaceType=org&maxResults=10" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" | python3 -m json.tool

# 3. 测试文件列表（验证 Storage.File.Read）
echo "=== 测试文件列表 ==="
curl -s -X POST "https://api.dingtalk.com/v1.0/storage/spaces/YOUR_SPACE_ID/dentries/listAll" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{"unionId": "YOUR_UNION_ID"}' | python3 -m json.tool

# 4. 测试文档写入（验证 Storage.File.Write）
echo "=== 测试文档写入 ==="
curl -s -X POST "https://api.dingtalk.com/v1.0/doc/suites/documents/YOUR_DOC_ID/overwriteContent?operatorId=YOUR_UNION_ID" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{"content": "# Test", "dataType": "markdown"}' | python3 -m json.tool
```

**成功标志：**
- 空间列表：返回 JSON 包含 `spaces` 数组
- 文件列表：返回 JSON 包含 `dentries` 数组
- 文档写入：返回 `{"result": {}, "success": true}`

**失败标志：**
- 返回 `errcode: 60011` 或 `code: Forbidden.AccessDenied` → 权限未开通
- 返回 `param.error` → 参数错误，检查 spaceId/dentryId 是否正确

---

## 🔗 快速申请链接

如果你的应用已经创建，可以直接使用以下链接（替换 `YOUR_APP_KEY`）：

```
https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.Space.Read
https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.Space.Write
https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.File.Read
https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.File.Write
https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.UploadInfo.Read
https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.DownloadInfo.Read
```

**注意：** "企业存储文件下载信息读权限" 需要在权限管理页面手动搜索申请，无直接链接。

---

## ⚠️ 常见问题

### Q1: 权限申请后多久生效？
**A:** 审批通过后 5-10 分钟内生效。如果长时间未生效，尝试重新获取 access_token。

### Q2: 提示 "企业认证等级过低"？
**A:** 部分高级权限需要企业完成实名认证。联系企业管理员完成认证。

### Q3: 旧版权限 `qyapi_cspace_base` 找不到？
**A:** 这是旧版权限，新版 API 已不再使用。可以忽略，不影响核心功能。

### Q4: 下载文件时提示权限不足？
**A:** 需要同时开通 `Storage.DownloadInfo.Read` 和 `企业存储文件下载信息读权限` 两个权限。

### Q5: 一个应用可以给多个企业使用吗？
**A:** 企业内部应用只能用于当前企业。如需多企业使用，需要：
- 为每个企业创建独立的应用
- 或使用第三方企业应用（需要上架）

---

## 📚 相关文档

- [钉钉开放平台 - 权限管理](https://open.dingtalk.com/document/orgapp-server/permission-management)
- [钉盘 API 文档](https://open.dingtalk.com/document/development/knowledge-base-download-file)
- [钉钉文档 API](https://open.dingtalk.com/document/development/dingtalk-document-model)
- [获取 access_token](https://open.dingtalk.com/document/orgapp-server/obtain-the-access_token-of-an-internal-app)

---

## 🆘 获取帮助

如遇问题，可以：
1. 查看钉钉开放平台官方文档
2. 在 [钉钉开发者社区](https://developers.dingtalk.com/) 提问
3. 联系技能开发者

---

*文档版本：1.0*  
*最后更新：2026-04-12*  
*适用技能：dingtalk-csa (钉盘助手)*


path: references/permission-list-share.md
content: # 钉钉应用权限开通清单

## 📋 应用信息

- **AppKey**: 配置为环境变量 `DINGTALK_APP_KEY`
- **AppSecret**: 配置为环境变量 `DINGTALK_APP_SECRET`（⚠️ 绝不要明文写在代码或文档中）
- **应用类型**: 企业内部应用

> ⚠️ **安全提示**：AppKey 和 AppSecret 是敏感凭据，请通过环境变量或安全配置文件传入，切勿硬编码。本文件中的申请链接使用 `YOUR_APP_KEY` 占位符，请替换为你自己的 AppKey。

---

## ✅ 已开通权限（6个）

| 权限 | 用途 |
|------|------|
| 钉盘应用盘空间读 | 列出和查看空间信息 |
| 钉盘应用盘空间写 | 创建新空间 |
| 钉盘应用文件读 | 列出和查询文件 |
| 钉盘应用文件写 | 上传文件、写入文档 |
| 钉盘上传信息读 | 获取上传凭证 |
| 钉盘下载信息读 | 获取文件下载信息 |

---

## ❌ 待开通权限（4个）

### 🔴 高优先级（必须开通）

| # | 权限名称 | 权限标识 | 用途 | 一键申请链接 |
|---|---------|---------|------|-------------|
| 1 | 企业存储文件下载信息读 | 企业存储文件下载信息读权限 | 下载文件到本地（配合已开通的"钉盘下载信息读"使用） | [点击申请](https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23%E4%BC%81%E4%B8%9A%E5%AD%98%E5%82%A8%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E4%BF%A1%E6%81%AF%E8%AF%BB%E6%9D%83%E9%99%90) |

### 🟡 中优先级（建议开通）

| # | 权限名称 | 权限标识 | 用途 | 一键申请链接 |
|---|---------|---------|------|-------------|
| 2 | 通讯录用户详情读 | qyapi_get_member | 获取用户手机号、邮箱等详细信息 | [点击申请](https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23qyapi_get_member) |
| 3 | 通讯录部门列表读 | qyapi_get_department_list | 获取组织架构部门列表 | [点击申请](https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23qyapi_get_department_list) |

### ⚪ 低优先级（可选）

| # | 权限名称 | 权限标识 | 用途 | 一键申请链接 |
|---|---------|---------|------|-------------|
| 4 | 企业钉盘基础权限 | qyapi_cspace_base | 旧版自定义空间API（新版API已覆盖大部分功能） | [点击申请](https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23qyapi_cspace_base) |

---

## 📝 开通步骤

### 方式一：一键申请（推荐）

1. 将链接中的 `YOUR_APP_KEY` 替换为你的实际 AppKey
2. 点击链接跳转到钉钉开放平台申请页面
3. 提交申请后等待管理员审批

### 方式二：手动搜索

1. 登录 [钉钉开放平台](https://open-dev.dingtalk.com/)
2. 进入 **应用开发** → 找到你的应用
3. 点击 **权限管理**
4. 在搜索框搜索权限名称（如"企业存储文件下载"）
5. 点击申请 → 管理员审批

---

## ⏱️ 生效时间

- 权限审批通过后 **5-10分钟** 内生效
- 如果测试仍提示权限不足，等待几分钟后重试

---

## 🧪 验证方法

权限开通后，可以运行以下测试验证：

```bash
# 获取token（使用你的环境变量）
APP_KEY="${DINGTALK_APP_KEY}"
APP_SECRET="${DINGTALK_APP_SECRET}"

[REDACTED_SECRET] -s -X POST 'https://api.dingtalk.com/v1.0/oauth2/accessToken' \
  -H 'Content-Type: application/json' \
  -d "{\"appKey\":\"$APP_KEY\",\"appSecret\":\"$APP_SECRET\"}" | \
  python3 -c "import json,sys; print(json.load(sys.stdin)['accessToken'])")

# 测试下载权限
curl -X POST "https://api.dingtalk.com/v1.0/storage/spaces/{spaceId}/dentries/{dentryId}/downloadInfos/query?unionId={unionId}" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{"option": {"version": 1}}'
```

返回下载URL表示权限开通成功。

---

## 📞 联系信息

如有疑问，请联系应用开发者或参考：
- 钉钉开放平台文档：https://open.dingtalk.com/document/
- 钉盘API文档：https://open.dingtalk.com/document/development/knowledge-base-download-file

---

*最后更新：2026-04-12*

path: references/permissions.md
content: # 钉钉开放平台权限开通指南

## 📋 应用信息

- **AppKey**: 配置为环境变量 `DINGTALK_APP_KEY`
- **AppSecret**: 配置为环境变量 `DINGTALK_APP_SECRET`（⚠️ 绝不要明文写在代码或文档中）
- **应用类型**: 企业内部应用
- **开通方式**: 管理员在钉钉开放平台操作

> ⚠️ **安全提示**：AppKey 和 AppSecret 是敏感凭据，请通过环境变量或安全配置文件传入，切勿硬编码。

## 🔑 权限状态总览

### ✅ 已开通的权限

| # | 权限名称 | 权限标识 | 用途 |
|---|---------|---------|------|
| 1 | 钉盘应用盘空间读 | Storage.Space.Read | 列出/查看空间信息 |
| 2 | 钉盘应用盘空间写 | Storage.Space.Write | 创建空间 |
| 3 | 钉盘应用文件读 | Storage.File.Read | 列出/查询文件 |
| 4 | 钉盘应用文件写 | Storage.File.Write | 上传文件、写入文档 |
| 5 | 钉盘上传信息读 | Storage.UploadInfo.Read | 获取上传凭证 |
| 6 | 通讯录部门用户读 | qyapi_get_department_member | 获取部门成员列表 |

### ❌ 待开通的权限

| # | 权限名称 | 权限标识 | 用途 | 优先级 |
|---|---------|---------|------|--------|
| 7 | 钉盘下载信息读 | Storage.DownloadInfo.Read | 下载文件到本地 | 🔴 高 |
| 8 | 通讯录用户详情读 | qyapi_get_member | 获取用户详细信息 | 🟡 中 |
| 9 | 通讯录部门列表读 | qyapi_get_department_list | 获取部门列表 | 🟡 中 |
| 10 | 个人用户信息读 | Contact.User.Read | 新版API读取用户信息 | 🟡 中 |
| 11 | 钉盘基础权限(旧版) | qyapi_cspace_base | 旧版自定义空间API | ⚪ 低 |

## 📝 开通流程

### 方式一：一键申请链接（推荐）

直接访问以下链接，逐个点击申请：

1. **Storage.DownloadInfo.Read**  
   https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Storage.DownloadInfo.Read

2. **qyapi_get_member**  
   https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23qyapi_get_member

3. **qyapi_get_department_list**  
   https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23qyapi_get_department_list

4. **Contact.User.Read**  
   https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23Contact.User.Read

5. **qyapi_cspace_base**（可选，旧版API）  
   https://open-dev.dingtalk.com/appscope/apply?content=YOUR_APP_KEY%23qyapi_cspace_base

> ⚠️ 将链接中的 `YOUR_APP_KEY` 替换为你的实际 AppKey。

### 方式二：在开放平台手动操作

1. 登录 [钉钉开放平台](https://open-dev.dingtalk.com/)
2. 进入 **应用开发** → 找到你的应用
3. 点击 **权限管理**
4. 搜索以下权限名称，逐个申请：
   - 搜索 "下载信息" → 开通 **钉盘应用下载信息读权限**
   - 搜索 "成员详情" → 开通 **通讯录用户详情读权限**
   - 搜索 "部门列表" → 开通 **通讯录部门列表读权限**
   - 搜索 "用户信息" → 开通 **个人用户信息读权限**
   - 搜索 "cspace" → 开通 **企业钉盘基础权限**（可选）
5. 企业管理员审批通过后权限立即生效

### 注意事项

- 企业内部应用的权限申请通常需要 **管理员审批**
- 权限开通后可能需要 **几分钟** 才能生效
- 搜索权限名称时如果在"钉盘"分类下找不到，尝试在"全部权限"中搜索
- `qyapi_cspace_base` 是旧版权限，如果搜不到可以忽略，新版API已覆盖其功能

## 🧪 验证方法

权限开通后，可以让Bayes运行以下命令验证：

```bash
# 获取token后测试
curl -s -X POST "https://api.dingtalk.com/v1.0/storage/spaces/{spaceId}/dentries/{dentryId}/downloadInfos/query" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{"unionId": "YOUR_UNION_ID"}'
```

如果返回下载链接而不是权限错误，说明开通成功。

path: references/upload-guide.md
content: # Upload Guide - 3-Step File Upload Process

## Overview

Uploading files to DingTalk Drive requires 3 steps:
1. **Get upload credentials** - Request signed URL and headers from DingTalk
2. **Upload to OSS** - PUT the file content to the provided URL
3. **Commit the file** - Notify DingTalk that upload is complete

## Step 1: Get Upload Credentials

```bash
curl -X POST "https://api.dingtalk.com/v2.0/storage/spaces/files/{parentDentryUuid}/uploadInfos/query?unionId={unionId}" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{
    "protocol": "HEADER_SIGNATURE",
    "option": {
      "storageDriver": "DINGTALK",
      "preCheckParam": {
        "size": {fileSizeInBytes},
        "name": "{fileName}"
      }
    }
  }'
```

**Returns:**
- `uploadKey` - Unique identifier for this upload (used in Step 3)
- `headerSignatureInfo.resourceUrls[0]` - OSS upload URL
- `headerSignatureInfo.headers` - Required headers (Authorization, x-oss-date)
- `expirationSeconds` - Credentials expire in 900 seconds (15 minutes)

**Getting parentDentryUuid:**
- For root directory: Use the folder's `uuid` field from `dentries/listAll` or `dentries/query`
- Root directory uuid can be queried with `dentryId: "0"`

## Step 2: Upload to OSS

```bash
curl -X PUT "{resourceUrl}" \
  -H "Authorization: {headers.Authorization}" \
  -H "x-oss-date: {headers.x-oss-date}" \
  -H "Content-Type:" \
  --data-binary @localfile.ext
```

**Important:**
- `Content-Type` header must be set to empty string (no content type)
- Use `--data-binary` for binary files
- HTTP 200 = success
- Credentials expire in 15 minutes, complete Step 3 before then

## Step 3: Commit the File

```bash
curl -X POST "https://api.dingtalk.com/v2.0/storage/spaces/files/{parentDentryUuid}/commit?unionId={unionId}" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{
    "uploadKey": "{uploadKey from Step 1}",
    "name": "{fileName}",
    "option": {
      "conflictStrategy": "AUTO_RENAME"
    }
  }'
```

**conflictStrategy options:**
- `AUTO_RENAME` - Auto rename if file exists (default)
- `OVERWRITE` - Overwrite existing file

**Returns:** File entry with `id`, `uuid`, `path`, `size`, etc.

## Creating a Text File Example

```bash
# 1. Create content
echo "Hello World" > /tmp/test.txt

# 2. Get upload credentials
RESP=$(curl -s -X POST "https://api.dingtalk.com/v2.0/storage/spaces/files/{parentUuid}/uploadInfos/query?unionId={unionId}" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d '{"protocol":"HEADER_SIGNATURE","option":{"storageDriver":"DINGTALK","preCheckParam":{"size":12,"name":"test.txt"}}}')

# 3. Extract values
UPLOAD_KEY=$(echo $RESP | python3 -c "import json,sys; print(json.load(sys.stdin)['uploadKey'])")
RESOURCE_URL=$(echo $RESP | python3 -c "import json,sys; print(json.load(sys.stdin)['headerSignatureInfo']['resourceUrls'][0])")
AUTH=$(echo $RESP | python3 -c "import json,sys; print(json.load(sys.stdin)['headerSignatureInfo']['headers']['Authorization'])")
DATE=$(echo $RESP | python3 -c "import json,sys; print(json.load(sys.stdin)['headerSignatureInfo']['headers']['x-oss-date'])")

# 4. Upload to OSS
curl -s -X PUT "$RESOURCE_URL" \
  -H "Authorization: $AUTH" \
  -H "x-oss-date: $DATE" \
  -H "Content-Type:" \
  --data-binary @/tmp/test.txt

# 5. Commit
curl -s -X POST "https://api.dingtalk.com/v2.0/storage/spaces/files/{parentUuid}/commit?unionId={unionId}" \
  -H "x-acs-dingtalk-access-[REDACTED_SECRET]" \
  -H 'Content-Type: application/json' \
  -d "{\"uploadKey\": \"$UPLOAD_KEY\", \"name\": \"test.txt\", \"option\": {\"conflictStrategy\": \"AUTO_RENAME\"}}"
```

## Large File Support

For files larger than 8MB, use multipart upload:
1. Call `/v1.0/storage/spaces/{spaceId}/files/multiPartUploadInfos/init` to initialize
2. Upload parts sequentially
3. Call commit to finalize

## Error Handling

- `400 param.error` - Check file size, name, or parentDentryUuid
- `403 Forbidden.AccessDenied` - Missing `Storage.File.Write` or `Storage.UploadInfo.Read` permission
- `403 no.priviledge` - User doesn't have write access to the folder
- Credentials timeout - Re-do Step 1 if 15 minutes have passed

Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (HIGH)