SuperAPI AI Directory

feishu-doc-writer

Office & productivity

View repository

Updated 10-16 09:45

About this skill

Feishu Document Writing Skill

This Skill is used to automatically write content to Feishu documents, with support for automatic Markdown format conversion.

Basic Concepts

Document Structure

A Feishu document is composed of multiple Blocks , and each block has a unique block_id. The document structure is as follows:

  • Document: identified by document_id , it is an online document
  • Block: the smallest building unit of a document, such as text, images, tables, etc.
  • Page Block: the root block of the document, whose block_id is the same as document_id Same

Get document_id

Get it from the document URL:

https://xxx.feishu.cn/docx/{document_id}

API Endpoints

1. Create a document

POST https://open.feishu.cn/open-apis/docx/v1/documents

Request body:

{
  "folder_token": "string", // 可选,文档所在文件夹 token
  "title": "string" // 可选,文档标题
}

Response example:

{
  "code": 0,
  "data": {
    "document": {
      "document_id": "TLLKdcpDro9ijQxA33ycNMabcef",
      "revision_id": 1,
      "title": "工作周报"
    }
  }
}

2. Create blocks (add child blocks at a specified position)

POST https://open.feishu.cn/open-apis/docx/v1/documents/{document_id}/blocks/{block_id}/children

Query parameters:

  • document_revision_id: document version, -1 means the latest version

Request body:

{
  "index": 0,           // 插入位置索引(0 表示第一个)
  "children": [...]     // Block 数组
}

3. Create nested blocks (create multi-level blocks at once)

POST https://open.feishu.cn/open-apis/docx/v1/documents/{document_id}/blocks/{block_id}/descendant

Query parameters:

  • document_revision_id: document version, -1 means the latest version

Request body:

{
  "index": 0,
  "children_id": ["block_id_1", "block_id_2"],  // 顶层子块 ID
  "descendants": [...]  // 所有块的详细定义
}

🚀 Quick Start

Method 1: Write directly to a Markdown file

# 设置环境变量后运行
FEISHU_APP_ID=xxx FEISHU_APP_SECRET=xxx node ./scripts/write-to-feishu.js <markdown_file> --title "文档标题"

# 示例
FEISHU_APP_ID=cli_xxx FEISHU_APP_SECRET=xxx node ./scripts/write-to-feishu.js README.md --title "项目介绍"

Method 2: Write to an existing document

FEISHU_APP_ID=xxx FEISHU_APP_SECRET=xxx node ./scripts/write-to-feishu.js content.md --doc-id <document_id>

Execution Flow

When the user needs to write to a Feishu document, execute the following steps:

1. Check credentials

Check whether the user has provided Feishu app credentials:

  • FEISHU_APP_ID: Feishu app ID
  • FEISHU_APP_SECRET: Feishu app secret

If the user has not provided them, you must ask the user to obtain the credentials.

2. Prepare content

Save the content provided by the user as a temporary Markdown file, or directly use the file specified by the user.

3. Execute the write command

Use the RunCommand tool to execute:

FEISHU_APP_ID=<用户提供的ID> FEISHU_APP_SECRET=<用户提供的密钥> node ./write-to-feishu.js <文件路径> --title "<标题>"

4. Return the result

Return the generated document link to the user.

Supported Markdown Syntax

The script automatically converts Markdown into Feishu Blocks:

Markdown Syntax Feishu Block
# 标题 Heading 1
## 标题 Heading 2
### 标题 Heading 3
- 列表 Unordered list
1. 列表 Ordered list
```code``` Code block (supports language highlighting)
> 引用 Quote block
--- Divider
**粗体** Bold text
*斜体* Italic text
`代码` Inline code
~~删除线~~ Strikethrough text
- [ ] 待办 To-do item
- [x] 已完成 Completed to-do
| 表格 | Table (standard Markdown table)
:::callout ... ::: Callout block (custom syntax)
:::grid cols=N ... ::: Columns (custom syntax)

Extended Syntax

Callout

:::callout color=green emoji=bulb
这是高亮块的内容,支持 **粗体** 和 _斜体_。
:::

Color options: red, orange, yellow, green, blue, purple, gray

Grid

:::grid cols=2
第一列内容
---col---
第二列内容
:::

Column limit: 2-4 columns, separated by ---col--- or :::col .

Table

Use standard Markdown table syntax:

| 列1 | 列2 | 列3 |
| --- | --- | --- |
| A1  | B1  | C1  |
| A2  | B2  | C2  |

Block Basic Structure

{
  "block_id": "string",
  "block_type": 2,
  "parent_id": "string",
  "children": ["child_block_id_1"],
  "text": { ... }  // 根据 block_type 对应的数据实体
}

Block Type Quick Reference

block_type Value Description
page 1 Page Block
text 2 Text Block
heading1 3 Heading 1
heading2 4 Heading 2
heading3 5 Heading 3
heading4 6 Heading 4
heading5 7 Heading 5
heading6 8 Heading 6
heading7 9 Heading 7
heading8 10 Heading 8
heading9 11 Heading 9
bullet 12 Bulleted List
ordered 13 Numbered List
code 14 Code Block
quote 15 Quote Block
todo 17 To-do
divider 22 Divider
image 27 Image Block
sheet 30 Spreadsheet
table 31 Table Block
table_cell 32 Table Cell
grid 34 Column Block
grid_column 35 Column
callout 19 Callout Block
quote_container 44 Quote Container

Block Structure Template

Text Block

{
  "block_type": 2,
  "text": {
    "elements": [{ "text_run": { "content": "文本内容" } }],
    "style": {
      "align": 1 // 1: 左对齐, 2: 居中, 3: 右对齐
    }
  }
}

Styled Text

{
  "block_type": 2,
  "text": {
    "elements": [
      {
        "text_run": {
          "content": "粗体",
          "text_element_style": { "bold": true }
        }
      },
      {
        "text_run": {
          "content": "斜体",
          "text_element_style": { "italic": true }
        }
      },
      {
        "text_run": {
          "content": "下划线",
          "text_element_style": { "underline": true }
        }
      }
    ]
  }
}

More text styles:

{
  "text_run": {
    "content": "带样式的文本",
    "text_element_style": {
      "bold": true, // 粗体
      "italic": true, // 斜体
      "underline": true, // 下划线
      "strikethrough": false, // 删除线
      "inline_code": false, // 行内代码
      "text_color": 1, // 文字颜色
      "background_color": 3, // 背景颜色
      "link": {
        // 链接
        "url": "https://example.com"
      }
    }
  }
}

Heading Block

{
  "block_type": 3,
  "heading1": {
    "elements": [{ "text_run": { "content": "标题内容" } }],
    "style": {
      "folded": false
    }
  }
}

Note:

  • block_type are 3-11 respectively, corresponding to heading1-heading9 respectively.
  • heading1 key names need to follow block_type and change accordingly:heading1, heading2, ... heading9。

Bulleted List

{
  "block_type": 12,
  "bullet": {
    "elements": [{ "text_run": { "content": "列表项" } }]
  }
}

Numbered List

{
  "block_type": 13,
  "ordered": {
    "elements": [{ "text_run": { "content": "列表项" } }]
  }
}

Code Block

{
  "block_type": 14,
  "code": {
    "elements": [{ "text_run": { "content": "代码内容" } }],
    "style": { "language": 22, "wrap": true }
  }
}

Common language enum values:

language Value
PlainText 1
Python 18
JavaScript 22
TypeScript 30
Java 13
Go 10
Rust 20
C 2
C++ 3
Shell 23
JSON 15
YAML 35
SQL 25
HTML 11
CSS 5
Markdown 17

Quote Block

{
  "block_type": 15,
  "quote": {
    "elements": [{ "text_run": { "content": "引用内容" } }]
  }
}

Divider

{
  "block_type": 22,
  "divider": {}
}

Callout Block (requires step-by-step creation)

The Callout Block itself cannot contain text content; its text content resides in its child blocks. When creating using nested blocks:

{
  "block_id": "callout_id",
  "block_type": 19,
  "callout": {
    "background_color": 4,
    "border_color": 4,
    "emoji_id": "bulb"
  },
  "children": ["callout_text_id"]
}

Color: red=1, orange=2, yellow=3, green=4, blue=5, purple=6, gray=7

To-do

{
  "block_type": 17,
  "todo": {
    "elements": [{ "text_run": { "content": "待办内容" } }],
    "style": { "done": false }
  }
}

Table (requires step-by-step creation)

Tables need to be created through the nested block API, including table, table_cell, and content blocks. To ensure order control and create multi-level blocks at once, using the descendant API is recommended:

{
  "index": 0,
  "children_id": ["table_id_1"],
  "descendants": [
    {
      "block_id": "table_id_1",
      "block_type": 31,
      "table": {
        "property": { "row_size": 2, "column_size": 3 }
      },
      "children": ["cell_1", "cell_2", "cell_3", "cell_4", "cell_5", "cell_6"]
    },
    {
      "block_id": "cell_1",
      "block_type": 32,
      "table_cell": {},
      "children": ["cell_1_text"]
    },
    {
      "block_id": "cell_1_text",
      "block_type": 2,
      "text": {
        "elements": [{ "text_run": { "content": "表头1" } }]
      },
      "children": []
    }
    // ... 其他单元格
  ]
}

After creation, get the cell_ids, then write content into each cell, or directly use the descendants API to write text content at creation time.

Columns (requires step-by-step creation)

Columns also require the nested block API; use descendant to create child columns and column content at once:

{
  "index": 0,
  "children_id": ["grid_id_1"],
  "descendants": [
    {
      "block_id": "grid_id_1",
      "block_type": 34,
      "grid": { "column_size": 2 },
      "children": ["column_1", "column_2"]
    },
    {
      "block_id": "column_1",
      "block_type": 35,
      "grid_column": { "width_ratio": 50 },
      "children": ["column_1_content"]
    },
    {
      "block_id": "column_1_content",
      "block_type": 2,
      "text": {
        "elements": [{ "text_run": { "content": "左栏内容" } }]
      },
      "children": []
    }
    // ... 第二栏
  ]
}

Column count limit: 2-4 columns. After creation, get the grid_column_ids, then write content into each column, or directly use the descendants API to build the entire tree at once.

Image Block

Images require two steps:

Step 1: Upload the image to the document

POST https://open.feishu.cn/open-apis/docx/v1/documents/{document_id}/blocks/{block_id}/media
Content-Type: multipart/form-data

file: (binary)
type: "image"

Step 2: Create the image block

{
  "block_type": 27,
  "image": {
    "token": "上传返回的 file_token",
    "width": 800,
    "height": 600
  }
}

Ensure writing order

Method 1: Call the API sequentially

Call the create block API one by one in order, using the index parameter to specify the position each time:

async function writeBlocksInOrder(documentId, parentBlockId, blocks) {
  for (let i = 0; i < blocks.length; i++) {
    await createBlock(documentId, parentBlockId, {
      index: i,
      children: [blocks[i]],
    });
  }
}

Method 2: Use the nested block API (recommended)

Use the descendant API to create multiple blocks at once, controlling order through the children_id array:

{
  "index": 0,
  "children_id": ["block_1", "block_2", "block_3"],  // 按顺序排列
  "descendants": [
    { "block_id": "block_1", ... },
    { "block_id": "block_2", ... },
    { "block_id": "block_3", ... }
  ]
}

Method 3: Batch create blocks

When creating multiple sibling blocks in a single request, thechildren array order is the display order:

{
  "index": 0,
  "children": [
    { "block_type": 3, "heading1": {...} },
    { "block_type": 2, "text": {...} },
    { "block_type": 12, "bullet": {...} }
  ]
}

Markdown to Block Conversion Rules

When the user provides Markdown content, convert it according to the following rules:

Markdown Block Type
# 标题 heading1 (3)
## 标题 heading2 (4)
### 标题 heading3 (5)
#### 标题 heading4 (6)
- 列表 or * 列表 bullet (12)
1. 列表 ordered (13)
```代码``` code (14)
> 引用 quote (15)
---or*** divider (22)
**粗体** text_element_style.bold
*斜体* text_element_style.italic
`代码` text_element_style.inline_code
~~删除~~ text_element_style.strikethrough
- [ ] 待办 todo (17, done=false)
- [x] 已完成 todo (17, done=true)
| A | B | table (31) + table_cell (32)
:::callout color=green callout (19) + text (2)
:::grid cols=2 grid (24) + grid_column (25)

Execution Example

When the user says "Help me create a Feishu document containing a project introduction":

  1. Ask for Feishu credentials (if not available)
  2. Generate a write script or directly execute the API call
  3. Create the document and write the content
  4. Return the document link to the user

Example of creating a complete document via API

const BASE_URL = "https://open.feishu.cn/open-apis";
const ACCESS_TOKEN = "your_access_token";

async function createDocWithBlocks() {
  // 1. 创建文档
  const docResponse = await fetch(`${BASE_URL}/docx/v1/documents`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${ACCESS_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ title: "示例文档" }),
  });
  const docData = await docResponse.json();
  const documentId = docData.data.document.document_id;

  // 2. 使用嵌套块 API 添加内容
  const blocks = {
    index: 0,
    children_id: ["heading", "text", "bullet1", "code", "callout"],
    descendants: [
      {
        block_id: "heading",
        block_type: 3,
        heading1: { elements: [{ text_run: { content: "文档标题" } }] },
        children: [],
      },
      {
        block_id: "text",
        block_type: 2,
        text: { elements: [{ text_run: { content: "这是一段正文内容。" } }] },
        children: [],
      },
      {
        block_id: "bullet1",
        block_type: 12,
        bullet: { elements: [{ text_run: { content: "要点一" } }] },
        children: [],
      },
      {
        block_id: "code",
        block_type: 14,
        code: {
          elements: [{ text_run: { content: 'console.log("Hello!");' } }],
          style: { language: 22, wrap: true },
        },
        children: [],
      },
      {
        block_id: "callout",
        block_type: 19,
        callout: { background_color: 4, border_color: 4, emoji_id: "bulb" },
        children: ["callout_text"],
      },
      {
        block_id: "callout_text",
        block_type: 2,
        text: { elements: [{ text_run: { content: "这是高亮块中的内容" } }] },
        children: [],
      },
    ],
  };

  await fetch(
    `${BASE_URL}/docx/v1/documents/${documentId}/blocks/${documentId}/descendant?document_revision_id=-1`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${ACCESS_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(blocks),
    },
  );

  console.log(`文档创建成功: ${documentId}`);
}

Notes

  1. API Limits: At least 200ms between each call to avoid 429 errors
  2. Permission Requirements: The application requires docx:document permission and needs to obtain tenant_access_token or user_access_token
  3. Nested Blocks: Highlight blocks, tables, etc. need to be created step by step. When using the nested block API, the custom block_id must be unique within the document
  4. Parent-Child Relationship Rules:
    • Only specific block types can serve as parent blocks (such as Page, Text, TableCell, etc.)
    • Page blocks cannot be added as child blocks
    • TableCell can only be added through table operations
  5. Table Limits:
    • Blocks such as Table and Sheet are not allowed to be added inside table cells
    • Grid blocks are not allowed to be added inside column containers
  6. Version Control: It is recommended to use document_revision_id=-1 to always operate on the latest version
  7. Error Handling: Pay attention to handling common error codes such as the 400177xxx series

Reference Links