feishu-doc-write

Feishu (Lark) Document API writing spec. Converts Markdown content to Feishu Block structures and writes to cloud docs. Handles concurrency ordering. Use when syncing articles, cre…

sunnyyao2222-eng

@sunnyyao2222-eng

Install

$ openclaw skills install @sunnyyao2222-eng/feishu-doc-write

Feishu Document Writer

Reference spec for writing content to Feishu (Lark) cloud documents via the Docx API. Feishu docs use a Block tree model — raw Markdown is not accepted.

Document (block_type=1, Page)
  +-- Heading1 Block (block_type=3)
  +-- Text Block (block_type=2)
  +-- Callout Block (block_type=19)
  |     +-- Text Block
  |     +-- Bullet Block
  +-- Image Block (block_type=27)
  +-- Divider Block (block_type=22)

Preferred Approach: Convert API

Feishu provides an official Markdown -> Blocks conversion endpoint:

POST /open-apis/docx/v1/documents/{document_id}/convert
{
  "content": "# Title\n\nBody text\n\n- Item 1\n- Item 2\n\n> Quote",
  "content_type": "markdown"
}

Pros: No manual Block JSON construction. Handles most standard Markdown. Limitation: Does not support Feishu-specific blocks (Callout, etc.) — use manual Block creation for those.

Block Type Reference

block_typeNameJSON KeyNotes
1PagepageDocument root
2TexttextParagraph
3-11Heading1-9heading1-heading9Headings
12BulletbulletUnordered list (each item = separate block)
13OrderedorderedOrdered list
14CodecodeCode block (with style.language enum)
15QuotequoteBlockquote
17TodotodoCheckbox item (with style.done)
19CalloutcalloutHighlight box (Feishu-specific, container block)
22DividerdividerHorizontal rule
27ImageimageTwo-step: create placeholder, then upload
31TabletableTable
34QuoteContainerquote_containerQuote container

Create Blocks API

POST /open-apis/docx/v1/documents/{document_id}/blocks/{block_id}/children?document_revision_id=-1

Headers:
  Content-Type: application/json
  Authorization: Bearer <tenant_access_token>

Body:
{
  "children": [ ...Block array... ],
  "index": 0
}
  • block_id: Parent block ID (usually document_id itself for root)
  • index: Insert position (0 = beginning, -1 or omit = end)

Block JSON Examples

Text

{
  "block_type": 2,
  "text": {
    "elements": [{
      "text_run": {
        "content": "Paragraph text here",
        "text_element_style": { "bold": false, "italic": false }
      }
    }]
  }
}

Heading

{ "block_type": 3, "heading1": { "elements": [{ "text_run": { "content": "H1 Title" } }] } }
{ "block_type": 4, "heading2": { "elements": [{ "text_run": { "content": "H2 Title" } }] } }

Bullet / Ordered List

{ "block_type": 12, "bullet": { "elements": [{ "text_run": { "content": "List item" } }] } }
{ "block_type": 13, "ordered": { "elements": [{ "text_run": { "content": "Numbered item" } }] } }

Each list item is a separate Block.

Code Block

{
  "block_type": 14,
  "code": {
    "elements": [{ "text_run": { "content": "console.log('hello');" } }],
    "style": { "language": 23, "wrap": false }
  }
}

Common language enums: PlainText=1, JavaScript=23, Python=40, TypeScript=49, Go=20, Shell=46, SQL=47, Java=22, Rust=44, C=12, CSS=17, HTML=21, Docker=19.

Callout (Feishu-specific highlight box)

Callout is a container block — create it first, then add child blocks inside.

// Step 1: Create callout as document child
{ "block_type": 19, "callout": { "background_color": 3, "border_color": 3, "emoji_id": "star" } }

// Step 2: POST .../blocks/{callout_block_id}/children
{ "children": [{ "block_type": 2, "text": { "elements": [{ "text_run": { "content": "Highlight text" } }] } }] }

Color enums: Red=1, Orange=2, Yellow=3, Green=4, Blue=5, Purple=6, Grey=7.

Divider

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

Image (two-step)

Step 1: Create placeholder block { "block_type": 27, "image": {} }
Step 2: Upload via POST /open-apis/drive/v1/medias/upload_all
  - multipart/form-data: file, file_name, parent_type="docx_image", parent_node=<image_block_id>

Text Styling

Apply styles via text_element_style in text_run:

PropertyTypeEffect
boldboolBold
italicboolItalic
strikethroughboolStrikethrough
underlineboolUnderline
inline_codeboolInline code
text_colorintText color (same enum as callout colors)
background_colorintBackground color
link.urlstringHyperlink

Multiple text_run elements in one block = mixed styles in one paragraph.

Markdown to Block Mapping

Markdownblock_typeJSON Key
# H13heading1
## H24heading2
### H35heading3
Paragraph2text
- item12bullet
1. item13ordered
Code fence14code
> quote15quote
- [ ] todo17todo
---22divider
![](url)27image (two-step)
**bold**--text_element_style.bold: true
*italic*--text_element_style.italic: true
`code`--text_element_style.inline_code: true
~~strike~~--text_element_style.strikethrough: true
[text](url)--text_element_style.link.url
(no MD equivalent)19callout (Feishu-specific)

Concurrency & Ordering (Critical)

Problem: Concurrent Block creation API calls produce random ordering.

Solution A: Single Batch Request (Recommended)

Put all blocks in one children array, single API call:

{
  "children": [
    { "block_type": 3, "heading1": { "elements": [{"text_run": {"content": "Title"}}] } },
    { "block_type": 2, "text": { "elements": [{"text_run": {"content": "Paragraph 1"}}] } },
    { "block_type": 22, "divider": {} },
    { "block_type": 4, "heading2": { "elements": [{"text_run": {"content": "Section 2"}}] } }
  ],
  "index": 0
}

Solution B: Serial Writes with Index

For long content requiring multiple requests, execute serially with explicit index:

Request 1: index=0, write block A
Request 2: index=1, write block B (wait for A to succeed)
Request 3: index=2, write block C (wait for B to succeed)

Solution C: Collect-Then-Write (Recommended)

LLM outputs complete Markdown -> Conversion layer -> Single API batch write

Never let the LLM write one paragraph at a time with concurrent API calls.

Complete Write Flow

  1. Create document: POST /open-apis/docx/v1/documents with { "folder_token": "<token>", "title": "Title" } -> returns document_id
  2. Build Block array: Convert full content to Block JSON
  3. Batch write: POST .../documents/{doc_id}/blocks/{doc_id}/children?document_revision_id=-1 with all blocks
  4. Container blocks (optional): For Callout etc., get block_id from step 3 response, then add children

Custom Callout Syntax

Since Markdown has no Callout equivalent, use this custom markup:

:::callout{color=yellow emoji=bulb}
Highlight content here.
Supports **bold**, *italic*, and lists.
:::
ParamValuesDefaultPurpose
colorred, orange, yellow, green, blue, purple, greyyellowBackground & border
emojiAny Feishu emoji_id (bulb, star, warning, fire)bulbLeft icon
borderSame as color valuesSame as colorBorder color (override)

Common templates:

:::callout{color=yellow emoji=bulb}
**Key Insight**: The most important takeaway
:::

:::callout{color=red emoji=warning}
**Warning**: Common misconception
:::

:::callout{color=green emoji=check}
**Action Item**: What to do next
:::

Rate Limits & Constraints

  • Max blocks per batch: ~50 recommended
  • Long articles: Split by H2/H3 sections, 200-500ms between batches
  • Always use document_revision_id=-1 (latest version)
  • Token validity: ~2 hours, cache and refresh before expiry

Authentication

curl -X POST 'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal' \
  -H 'Content-Type: application/json' \
  -d '{ "app_id": "<app_id>", "app_secret": "<app_secret>" }'

Schema Pitfalls (Battle-tested)

  • No Markdown tables in write ops — use bullet lists instead (prevents schema errors)
  • No nested code blocks inside lists — Feishu schema validation is strict on nesting depth
  • Callout is a container — always requires a two-step create (container first, then children)
  • Each list item = separate Block — don't try to put multiple items in one block

References

Top skills in this category

Proactive Agent Lite

@bestrocky

Transform AI agents from task-followers into proactive partners with memory architecture, reverse prompting, and self-healing patterns. Lightweight version f...

6241k

腾讯文档 TENCENT DOCS

@liyang58

腾讯文档(docs.qq.com)-在线云文档平台,是创建、编辑、管理文档的首选 skill。涉及"新建/创建/编辑/读取/查看/搜索文档"、"保存文件"、"云文档"、"腾讯文档"、"docs.qq.com"等操作,请优先使用本 skill。支持能力:(1) 创建各类在线文档(文档/Word/Excel/幻灯片/...

1919k

Figma

@maddiedreese

Professional Figma design analysis and asset export. Use for extracting design data, exporting assets in multiple formats, auditing accessibility compliance, analyzing design systems, and generating comprehensive design documentation. Read-only analysis of Figma files with powerful export and reporting capabilities.

1610.0k

Smart Model Switching

@millibus

Auto-route tasks to the cheapest Claude model that works correctly. Three-tier progression: Haiku → Sonnet → Opus. Classify before responding. HAIKU (default): factual Q&A, greetings, reminders, status checks, lookups, simple file ops, heartbeats, casual chat, 1-2 sentence tasks. ESCALATE TO SONNET: code >10 lines, analysis, comparisons, planning, reports, multi-step reasoning, tables, long writing >3 paragraphs, summarization, research synthesis, most user conversations. ESCALATE TO OPUS: architecture decisions, complex debugging, multi-file refactoring, strategic planning, nuanced judgment, deep research, critical production decisions. Rule: If a human needs >30 seconds of focused thinking, escalate. If Sonnet struggles with complexity, go to Opus. Save 50-90% on API costs by starting cheap and escalating only when needed.

216.8k

AI Daily Briefing

@jeffjhunter

Start every day focused. Get a morning briefing with overdue tasks, today's priorities, calendar overview, and context from recent meetings. Works with ai-meeting-notes to-do list. No setup. Just say 'briefing'.

206.8k