Feishu Block Operations
Direct REST API operations for Feishu cloud documents when the feishu_doc tool's built-in actions don't cover your needs.
When to Use This (vs feishu_doc)
| Need | Use |
|---|
| Read/write/append document | INLINECODE1 |
| Create simple table |
feishu_doc create_table_with_values |
| Upload image/file |
feishu_doc upload_image/
upload_file |
|
Batch update 200 cells at once |
This skill |
|
Insert content at exact position |
This skill (or
feishu-md2blocks) |
|
Traverse block tree |
This skill |
|
Table row/column insert/delete |
This skill |
|
Merge/unmerge table cells |
This skill |
|
Replace images in-place |
This skill |
|
Delete blocks by index range |
This skill |
Authentication
Get tenant access token from OpenClaw config:
CODEBLOCK0
All API calls use header: INLINECODE8
Rate Limits
| Operation | Limit |
|---|
| Read (GET) | 5 req/sec per app |
| Write (POST/PATCH/DELETE) |
3 req/sec per app, 3 req/sec per document |
Use time.sleep(0.35) between write calls. For reads, time.sleep(0.25).
API Reference
Base URL: INLINECODE11
1. Get Block
CODEBLOCK1
Returns single block with full content (type, elements, children IDs, styles).
2. Get Children (with optional full tree)
CODEBLOCK2
Tip: Use with_descendants=true on table blocks to get all cells + cell content in one call.
3. Create Blocks (simple, flat only)
CODEBLOCK3
- - Max 50 blocks per call
- Cannot create nested structures (e.g. table with cell content)
- INLINECODE13 in body: 0=beginning, -1=end (default)
4. Create Nested Blocks (tables, grids, etc.)
CODEBLOCK4
- - Max 1000 blocks per call
- INLINECODE14 : only first-level child IDs (NOT grandchildren — causes error 1770006)
- INLINECODE15 : flat array of ALL blocks including nested ones, each with
block_id, block_type, children (list of child temp IDs) - ⚠️
index MUST be in request body, NOT as URL query parameter — ?index=N is silently ignored
5. Batch Update Blocks
CODEBLOCK5
Max 200 blocks per call. Each request object contains block_id + one operation:
| Operation | Purpose |
|---|
| INLINECODE22 | Replace text content + inline elements |
| INLINECODE23 |
Change alignment, folded, language, wrap, background_color |
|
update_table_property | Modify column widths, header rows/columns |
|
insert_table_row | Insert rows at index |
|
insert_table_column | Insert columns at index |
|
delete_table_rows | Delete rows by index + count |
|
delete_table_columns | Delete columns by index + count |
|
merge_table_cells | Merge cells (row
start, rowend, column
start, columnend) |
|
unmerge_table_cells | Unmerge previously merged cells |
|
replace_image | Replace image block's content with new file_token |
Example: batch update text in multiple cells
CODEBLOCK6
6. Update Single Block
CODEBLOCK7
7. Delete Blocks
CODEBLOCK8
- - ⚠️ Uses
start_index/end_index (half-open interval [start, end)), NOT INLINECODE35 - Indices are relative to the parent block's children list
Block Types
| Type | ID | Notes |
|---|
| Page | 1 | Document root, always one |
| Text |
2 | Plain paragraph |
| Heading1–9 | 3–11 | |
| Bullet | 12 | Unordered list item |
| Ordered | 13 | Ordered list item |
| Code | 14 | Code block |
| Quote | 15 | Block quote |
| Todo | 17 | Checkbox item |
| Callout | 19 | Highlighted block |
| Divider | 22 | Horizontal rule (body:
{}) |
| Grid | 24 | Multi-column layout |
| GridColumn | 25 | Column in grid |
| Image | 27 | Image block |
| Table | 31 | Table container |
| TableCell | 32 | Cell in table |
| QuoteContainer | 34 | Quote wrapper (body:
{}) |
Text Elements
Text blocks contain an elements array. Each element is one of:
CODEBLOCK9
Common Patterns
Pattern 1: Read table content
CODEBLOCK10
Pattern 2: Insert Markdown at position
Use feishu-md2blocks skill's md2blocks.py script with --after <block_id>.
Or manually:
CODEBLOCK11
Pattern 3: Batch edit table cells
CODEBLOCK12
Pattern 4: Delete then re-insert (position workaround)
When you need to replace content at a specific position:
CODEBLOCK13
Gotchas & Lessons Learned
- 1.
/descendant index in body, not URL — The most common pitfall. ?index=N compiles but is silently ignored. batch_delete uses index range — {"start_index": 0, "end_index": 5} deletes children[0..4]. Do NOT pass block_ids.- Table
merge_info is read-only — Must strip from blocks before insertion or API returns error. children_id is first-level only — Including grandchild IDs in children_id causes error 1770006.- Rate limit is per-document — Multiple concurrent edits to the same doc share the 3/sec limit.
with_descendants=true saves calls — One GET instead of N+1 for reading table content.- Convert API returns temp IDs — After insertion, actual block IDs differ from the temp IDs used during convert.
飞书块操作
当 feishu_doc 工具的内置操作无法满足需求时,直接使用 REST API 操作飞书云文档。
何时使用此技能(对比 feishu_doc)
| 需求 | 使用工具 |
|---|
| 读取/写入/追加文档 | feishudoc |
| 创建简单表格 |
feishudoc create
tablewith_values |
| 上传图片/文件 | feishu
doc uploadimage/upload_file |
|
一次性批量更新200个单元格 |
此技能 |
|
在精确位置插入内容 |
此技能(或 feishu-md2blocks) |
|
遍历块树 |
此技能 |
|
表格行/列插入/删除 |
此技能 |
|
合并/取消合并表格单元格 |
此技能 |
|
原地替换图片 |
此技能 |
|
按索引范围删除块 |
此技能 |
身份认证
从 OpenClaw 配置中获取租户访问令牌:
python
import json, urllib.request
def getfeishutoken():
with open(os.path.expanduser(~/.openclaw/openclaw.json)) as f:
c = json.load(f)[channels][feishu]
payload = json.dumps({appid: c[appId], appsecret: c[appSecret]}).encode()
req = urllib.request.Request(
https://open.feishu.cn/open-apis/auth/v3/tenantaccesstoken/internal,
data=payload, headers={Content-Type: application/json}, method=POST)
return json.loads(urllib.request.urlopen(req).read())[tenantaccesstoken]
所有 API 调用使用请求头:Authorization: Bearer {token}
速率限制
| 操作 | 限制 |
|---|
| 读取(GET) | 每个应用 5 次/秒 |
| 写入(POST/PATCH/DELETE) |
每个应用 3 次/秒,每个文档 3 次/秒 |
写入调用之间使用 time.sleep(0.35)。读取调用使用 time.sleep(0.25)。
API 参考
基础 URL:https://open.feishu.cn/open-apis/docx/v1/documents
1. 获取块
GET /docx/v1/documents/{doc}/blocks/{block_id}
返回包含完整内容的单个块(类型、元素、子节点 ID、样式)。
2. 获取子节点(可选完整树)
GET /docx/v1/documents/{doc}/blocks/{block_id}/children
?with_descendants=true # 获取所有后代,不仅直接子节点
&page_size=500 # 最大 500
&documentrevisionid=-1 # 最新版本
提示: 在表格块上使用 with_descendants=true 可一次性获取所有单元格及单元格内容。
3. 创建块(简单、仅平级)
POST /docx/v1/documents/{doc}/blocks/{parent_id}/children
请求体:{children: [...blocks], index: 0}
- - 每次调用最多 50 个块
- 不能创建嵌套结构(例如带单元格内容的表格)
- 请求体中的 index:0=开头,-1=末尾(默认)
4. 创建嵌套块(表格、网格等)
POST /docx/v1/documents/{doc}/blocks/{parent_id}/descendant
请求体:{
childrenid: [tempid1, tempid_2],
descendants: [...所有包含父子关系的块],
index: 0
}
- - 每次调用最多 1000 个块
- childrenid:仅第一级子节点 ID(不是孙节点——会导致错误 1770006)
- descendants:包含所有嵌套块的扁平数组,每个块包含 blockid、block_type、children(子节点临时 ID 列表)
- ⚠️ index 必须在请求体中,而不是 URL 查询参数——?index=N 会被静默忽略
5. 批量更新块
PATCH /docx/v1/documents/{doc}/blocks/batch_update
请求体:{requests: [...update_requests]}
每次调用最多 200 个块。每个请求对象包含 block_id + 一个操作:
| 操作 | 用途 |
|---|
| updatetextelements | 替换文本内容 + 内联元素 |
| updatetextstyle |
更改对齐、折叠、语言、换行、背景颜色 |
| update
tableproperty | 修改列宽、表头行/列 |
| insert
tablerow | 在索引处插入行 |
| insert
tablecolumn | 在索引处插入列 |
| delete
tablerows | 按索引+数量删除行 |
| delete
tablecolumns | 按索引+数量删除列 |
| merge
tablecells | 合并单元格(行开始、行结束、列开始、列结束) |
| unmerge
tablecells | 取消合并之前合并的单元格 |
| replace
image | 用新的 filetoken 替换图片块内容 |
示例:批量更新多个单元格中的文本
python
requests = []
for blockid, newtext in updates.items():
requests.append({
blockid: blockid,
updatetextelements: {
elements: [{textrun: {content: newtext}}]
}
})
api_call(token, PATCH,
fhttps://open.feishu.cn/open-apis/docx/v1/documents/{doc}/blocks/batch_update,
{requests: requests})
6. 更新单个块
PATCH /docx/v1/documents/{doc}/blocks/{block_id}
请求体:{与 batchupdate 相同的操作,不包含 blockid 包装}
7. 删除块
DELETE /docx/v1/documents/{doc}/blocks/{parentid}/children/batchdelete
请求体:{startindex: 0, endindex: 5}
- - ⚠️ 使用 startindex/endindex(半开区间 [start, end)),不是 block_ids
- 索引相对于父块的子节点列表
块类型
2 | 普通段落 |
| 标题1–9 | 3–11 | |
| 无序列表 | 12 | 无序列表项 |
| 有序列表 | 13 | 有序列表项 |
| 代码 | 14 | 代码块 |
| 引用 | 15 | 块引用 |
| 待办 | 17 | 复选框项 |
| 高亮块 | 19 | 高亮块 |
| 分割线 | 22 | 水平分割线(请求体:{}) |
| 网格 | 24 | 多列布局 |
| 网格列 | 25 | 网格中的列 |
| 图片 | 27 | 图片块 |
| 表格 | 31 | 表格容器 |
| 表格单元格 | 32 | 表格中的单元格 |
| 引用容器 | 34 | 引用包装器(请求体:{}) |
文本元素
文本块包含一个 elements 数组。每个元素是以下之一:
python
纯文本
{text
run: {content: hello, textelement_style: {bold: True, link: {url: ...}}}}
@用户
{mention
user: {userid: ou
xxx, textelement_style: {}}}
@文档
{mention
doc: {token: xxx, objtype: 22, text
elementstyle: {}}}
公式(LaTeX)
{equation: {content: E=mc^2}}
提醒
{reminder: {expire
time: 1234567890, iswhole_day: True}}
常见模式
模式 1:读取表格内容
python
1. 一次性获取表格的后代
url = f.../blocks/{table
blockid}/children?with
descendants=true&pagesize=500&document
revisionid=-1
items = api_call(token, GET, url)[data][items]
2. 从单元格中提取文本
for item in items:
if item[block_type] == 2 and text in item:
text = .join(e.get(text_run, {}).get(content, ) for e in item[text][elements])
模式 2:在位置插入