SuperAPI AI Directory

feishu-cli-msg

Office & productivity

View repository

Updated 10-15 15:35

About this skill

Feishu Message Sending Skill

Send Feishu messages, replies, forwards, merged forwards, urgent notifications, and download message resources via feishu-cli.

feishu-cli: If not yet installed, please go to riba2534/feishu-cli to get the installation method.

Boundary of responsibilities with the feishu-cli-chat skill

Important: CLI path ≠ SKILL ownership. feishu-cli msg The subcommands under it are managed by two SKILLs at the same time,divided by action type, not by CLI path.

Action type Subcommand Owning SKILL
Sending type(covered by this SKILL) send / reply / forward / merge-forward / urgent / flag / resource-download feishu-cli-msg
Reading type(please use the chat SKILL) history / list / get / mget / thread-messages / search-chats / read-users / pins feishu-cli-chat
Interaction type(please use the chat SKILL) reaction / pin / unpin / delete feishu-cli-chat

To pull group messages over a time window end-to-end (including thread expansion, name resolution, card parsing), just run:

python3 skills/feishu-cli-chat/scripts/fetch_chat_history.py oc_xxx --since 24h

Core concepts

Message architecture

The content field of the Feishu message API is a JSON string(not a JSON object). The CLI provides three input methods:

Input method Parameter Applicable scenario
Quick text --text "内容" Plain text message, the simplest
Send file --file <路径> or -f Local file automatically uploaded and sent (limit 30MB)
Send image --image <路径> Local image automatically uploaded and sent (limit 10MB)
Inline JSON --content '{"key":"val"}' or -c Simple JSON, done in one line
JSON file --content-file file.json Complex messages (cards, rich text, etc.)

Mutually exclusive: The above 5 input methodsonly one can be specified, specifying more than one at the same time will cause an error.

Receiver type

--receive-id-type Description Example
email Email address user@example.com
open_id Open ID ou_xxx
user_id User ID xxx
union_id Union ID on_xxx
chat_id Group chat ID oc_xxx

Message type selection

Decision tree (automatically selected when Claude does not specify a type)

By default, prefer interactive(card message), which is visually appealing, rich in content, and supports colors/multiple columns/buttons, etc.

用户需求
├─ 【默认】通知/报告/告警/任何有信息量的消息 → interactive(卡片)
├─ 发送已上传的图片/文件/音视频 → image/file/audio/media
├─ 分享群聊或用户名片 → share_chat/share_user
└─ 仅以下场景才用 text/post:
   ├─ 用户明确要求发纯文本 → text
   └─ 用户明确要求发富文本 → post

Why prefer cards: text does not support any format rendering, post has limited styling, cards support colored headers, multi-column fields, buttons, dividers, notes, etc., and their visual effect is far superior to other types.

Message type overview

Type Description content format Size limit
text Plain text {"text":"内容"} 150 KB
post Rich text {"zh_cn":{"title":"","content":[[...]]}} 150 KB
image Image {"image_key":"img_xxx"} —
file File {"file_key":"file_v2_xxx"} —
audio Voice {"file_key":"file_v2_xxx"} —
media Video {"file_key":"...","image_key":"..."} —
sticker Sticker {"file_key":"file_v2_xxx"} Forward only
interactive Card Card JSON / template_id / card_id 30 KB
share_chat Group card {"chat_id":"oc_xxx"} —
share_user Personal card {"user_id":"ou_xxx"} —

system System divider (p2p only) CLI --msg-type Not yet included in the whitelist (see cmd/send_message.go:261-266), when needed use feishu-cli api POST /open-apis/im/v1/messages to pass through directly.

Identity description

This skill is mainly based onsend-type commands, and by default uses App Token (Bot identity), no login required.

  • User Token required:msg flag favorite/bookmark subcommands (im:feed.flag:read/write), see the "Message bookmarks" section at the end.
  • Prefer User Token + Tenant fallback:msg resource-download When logged in, automatically use User Token to download (requires that you can see the message); if not logged in, try App Token (requires that the Bot can see the message).

For the Token strategy of other subcommands (reaction/pin/delete/history/list/get/mget/thread-messages/search-chats), see the feishu-cli-chat skill.

Send commands

Basic format

feishu-cli msg send \
  --receive-id-type <type> \
  --receive-id <id> \
  [--msg-type <msg_type>] \
  [--text "<text>" | --file <path> | --image <path> | --content '<json>' | --content-file <file.json>]

file type (send file directly)

# 直接发送本地文件(自动上传,限 30MB)
feishu-cli msg send \
  --receive-id-type email \
  --receive-id user@example.com \
  --file /path/to/report.pdf

Automatically infer the file MIME type (opus/mp4/pdf/doc/xls/ppt); for unknown types use stream. For files larger than 30MB, first use file upload to upload to cloud storage, then use --msg-type file --content '{"file_key":"..."}' to send.

image type (send image directly)

# 直接发送本地图片(自动上传,限 10MB)
feishu-cli msg send \
  --receive-id-type chat_id \
  --receive-id oc_xxx \
  --image /path/to/screenshot.png

Supports JPEG, PNG, BMP, GIF, TIFF, WebP formats.

text type

# 最简形式(默认 msg-type 为 text)
feishu-cli msg send \
  --receive-id-type email \
  --receive-id user@example.com \
  --text "你好,这是一条测试消息"

Inline @ syntax supported by the text type:

  • @ User:<at user_id="ou_xxx">Tom</at> —— ou_xxx must be a real open_id
  • @ Everyone:<at user_id="all"></at>
  • @ Bot: same syntax as @ user, just replace ou_xxx with the bot open_id

Only open_id can be used:<at email="..."> In a text message,will not trigger an @ entity(the Feishu IM client will automatically render the email string as a hyperlink, which looks like an @ but has no notification and is not a real mention). To @ an email user, first look up the open_id:

# 第一步:查 open_id
feishu-cli user search --email alice@example.com -o json
# 第二步:用真实 open_id @ 人
feishu-cli msg send --receive-id-type chat_id --receive-id oc_xxx \
  --text '<at user_id="ou_xxx">Alice</at> 你好'

Fault tolerance (only --text mode):msg send / msg reply In --text mode, it will automatically correct @ tag formats that AI easily writes incorrectly; the following forms will all be normalized to the standard <at user_id="...">:

  • <at id=ou_xxx>(missing quotes / using id instead of user_id)
  • <at open_id="ou_xxx"/>(self-closing / using open_id)
  • <at user_id=ou_xxx/>(self-closing without quotes)

--content / --content-file modedoes not perform implicit normalize(users are responsible for the JSON they write themselves, to avoid breaking the structure).

Note: text typedoes not supportrich text styles (bold, italic, underline, strikethrough, hyperlinks, etc. will not be rendered). If formatting is needed, please use the post type.

post type (rich text)

Recommended to use the md tag to carry Markdown; one md tag occupies one paragraph:

cat > /tmp/msg.json << 'EOF'
{
  "zh_cn": {
    "title": "项目进展通知",
    "content": [
      [{"tag": "md", "text": "**本周进展**\n- 完成功能 A 开发\n- 修复 3 个 Bug\n- [查看详情](https://example.com)"}],
      [{"tag": "md", "text": "**下周计划**\n1. 功能 B 开发\n2. 性能优化"}]
    ]
  }
}
EOF

feishu-cli msg send \
  --receive-id-type email \
  --receive-id user@example.com \
  --msg-type post \
  --content-file /tmp/msg.json

Tag types supported by post:

tag Description Main attributes
text Text text, style(bold/italic/underline/lineThrough)
a Hyperlink text, href
at @User user_id, user_name
img Image image_key, width, height
media Video file_key, image_key
emotion Emoji emoji_type
hr Divider —
code_block Code block language, text
md Markdown text (occupies a paragraph, recommended)

Automatic image upload (--upload-images)

msg send Supports --upload-images, scans post / interactive Markdown image syntax in messages ![alt](path), and path pointing tolocal imagesautomatically uploads to the Feishu IM image host and replaces them with image_keybefore sending.

Dimension Behavior
Trigger condition Only takes effect when --msg-type post or --msg-type interactive (other types are ignored even if passed, see cmd/send_message.go:212)
Applicable syntax the ![alt](path) marker in the content; URLs (http/https) and existing image_key will not be rewritten
Path resolution Relative path: use the directory containing --content-file as basePath; when using --content with inline JSON, usecurrent working directoryas basePath (see cmd/send_message.go:213-220)
Failure fallback On upload failure, error out directly and do not continue sending an incomplete message (to avoid the server rejecting it due to a missing image_key)
Progress indication When uploading > 0 images, print to stderr 已自动上传 N 张本地图片
# post 内嵌本地图:自动上传相对路径 ./diagrams/foo.png
feishu-cli msg send --receive-id-type email --receive-id user@example.com \
  --msg-type post --content-file /path/to/post.json --upload-images

# interactive 卡片内嵌本地图同理
feishu-cli msg send --receive-id-type chat_id --receive-id oc_xxx \
  --msg-type interactive --content-file /tmp/card.json --upload-images

No need to call feishu-cli media uploadin advance: this flag already wraps the full flow of "upload + replace + send". To send an image as a standalone image message, use the --image <path> shortcut directly.

interactive type (card message)

There are three ways to send card messages:

Method 1: Full Card JSON (send only; for complex cards, first generate with feishu-cli-card)

cat > /tmp/card.json << 'EOF'
{
  "schema": "2.0",
  "header": {
    "template": "blue",
    "title": {"tag": "plain_text", "content": "任务完成通知"}
  },
  "body": {
    "direction": "vertical",
    "elements": [
      {"tag": "markdown", "content": "**项目**: feishu-cli\n**状态**: 已完成\n**负责人**: <at id=all></at>"}
    ]
  }
}
EOF

feishu-cli msg send \
  --receive-id-type email \
  --receive-id user@example.com \
  --msg-type interactive \
  --content-file /tmp/card.json

Method 2: template_id

cat > /tmp/card.json << 'EOF'
{
  "type": "template",
  "data": {
    "template_id": "AAqk1xxxxxx",
    "template_variable": {"name": "张三", "status": "已完成"}
  }
}
EOF

feishu-cli msg send \
  --receive-id-type email \
  --receive-id user@example.com \
  --msg-type interactive \
  --content-file /tmp/card.json

Method 3: card_id

feishu-cli msg send \
  --receive-id-type email \
  --receive-id user@example.com \
  --msg-type interactive \
  --content '{"type":"card","data":{"card_id":"7371713483664506900"}}'

Interactive card responsibility boundaries

This skill is only responsible for sending interactive messages, not for designing card JSON.

  • Structured or visually appealing cards must first be generated as v2 JSON (schema=2.0) using feishu-cli-card.
  • This skill sends: feishu-cli msg send --msg-type interactive --content-file <card.json>.
  • Do not write new v1 elements/action/note card templates within this skill; the old v1 examples are only for historical compatibility troubleshooting.

Execution flow

Message sending flow

  1. Determine the recipient: default user@example.com(email), or obtain from context
  2. Choose the message type:
    • User explicitly specifies a type → use the specified type
    • Structured or visually appealing notification → first use feishu-cli-card to construct JSON, then use interactive to send
    • User explicitly requests plain text/rich text, or the content is very short → use text / post
  3. Prepare the content: for plain text, pass directly to --text; for card JSON, use --content-file; for files/images, use --file / --image
  4. Send and check the result: execute the command and confirm the returned message_id

Permission requirements

Permission Description
im:message Message read/write (send/reply/forward)
im:message:send_as_bot Send messages as a bot

Notes

Limit Description
text size limit Maximum 150 KB per message
Card/rich text size limit Maximum 30 KB per message
system message CLI --msg-type Not supported yet; only valid for p2p sessions; requires feishu-cli api passthrough
sticker message Only forwarding received stickers is supported; uploading your own is not supported
Card button callback Button interaction callbacks require application server-side support; for buttons sent via CLI, only URL redirects are effective
API rate limit Requests that are too fast return 429; wait a few seconds and retry
Delete message Only messages sent by the bot can be deleted

Error handling

Error Cause Solution
content format of a post type is incorrect post type JSON format error Ensure the format is {"zh_cn":{"title":"","content":[[...]]}}
invalid receive_id Invalid receiver ID Check whether --receive-id-type and --receive-id match
bot has no permission Bot has no permission Confirm the application has the im:message:send_as_bot permission
rate limit exceeded API rate limiting Wait a few seconds and retry
user not found User does not exist Check whether the email or ID is correct
card content too large Card JSON exceeds 30 KB Simplify the card content or split it into multiple messages
Bot/User can NOT be out of the chat Bot is not in the target group Add --user-access-token switch to User identity and retry

Batch get messages

To read message details and batch get messages, use the feishu-cli-chat skill.msg get/list/history/mget By default requests user_card_content and additionally extracts card_texts; this behavior and troubleshooting instructions are maintained in the chat skill to avoid mixing sending and reading responsibilities.

Download message resources

Download image or file attachments in messages.

# 下载消息中的图片
feishu-cli msg resource-download <message_id> <file_key> --type image -o /tmp/photo.png

# 下载消息中的文件
feishu-cli msg resource-download <message_id> <file_key> --type file -o /tmp/attachment.pdf

# Bot 不可见但当前用户可见的历史消息资源,可显式用 User Token
feishu-cli msg resource-download <message_id> <file_key> --type file --user-access-token u-xxx -o /tmp/attachment.pdf

# 下载大文件时指定超时时间
feishu-cli msg resource-download <message_id> <file_key> --type file --user-access-token u-xxx -o /tmp/large.bin --timeout 30m
Parameter Description Default
<message_id> Message ID Required
<file_key> Resource file_key Required
--type Resource type image/file Required
-o, --output Output file path —
--user-access-token Use user identity to download historical message resources that the user can see but the Bot cannot —
--timeout Download timeout (Go duration format, e.g. 10m、30m、1h) 5m

file_key source: via msg get <message_id> get message details, and extract from content image_key or file_key. When downloading directly with user identity, if you encounter Feishu large file limits, HTTP Range chunked download and merge will be used automatically.

Thread message

Send to an existing thread

msg send supports --thread-id(equivalent to --receive-id-type thread_id --receive-id <thread_id>):

# 在已有话题内追加一条消息
feishu-cli msg send --thread-id omt_xxx --text "话题内继续聊"

# 卡片消息也支持
feishu-cli msg send --thread-id omt_xxx \
  --msg-type interactive \
  --content "$(cat card.json)"

--thread-id and --receive-id-type/--receive-id are mutually exclusive; only one group can be specified.

Start a thread when replying

msg reply supports --reply-in-thread(reply_in_thread=true):

# 在非话题群聊中,以话题形式回复某条消息(会开启一个新话题)
feishu-cli msg reply om_xxx --text "这里开个话题" --reply-in-thread

# 若群聊已是话题模式,--reply-in-thread 会自动回复到消息所在话题

Thread reply list

Getting thread replies is reading messages, please use the feishu-cli-chat skill. Sending messages within a thread still uses this skill's msg send --thread-id。

Reference documentation

  • references/message_content.md: detailed explanation of the content JSON structure for each message type
  • references/card_schema.md: interactive sending format and historical card troubleshooting; for new card construction, prefer feishu-cli-card

Message bookmark (msg flag, added in v1.23+)

The server calls it a message flag, used to add a Feed mark to a message and push the message to the user's Feed/bookmarks. Corresponds to HTTP API POST/GET/PATCH /open-apis/im/v1/flags. Requires User Access Token:list requires im:feed.flag:read,create/cancel requires im:feed.flag:write。

Command quick reference

  • feishu-cli msg flag create <message_id> — create a message-level bookmark (default --item-type default --flag-type message)
  • feishu-cli msg flag create <message_id> --flag-type feed — feed-level bookmark, automatically reads chat_mode to determine thread / msg_thread
  • feishu-cli msg flag create <message_id> --item-type msg_thread --flag-type feed — feed-level bookmark (explicitly specifies a normal group message thread)
  • feishu-cli msg flag list [--page-size 50] [--page-token xxx] — list the current user's bookmarks
  • feishu-cli msg flag cancel <message_id> [--item-type ... --flag-type ...] — by default tries to cancel both the message level and the feed level; when item/flag is explicitly passed, only the specified level is canceled

Key enums (server-side ground truth, never fabricate by the order 0/1/2)

CLI flags use strings, mapped underneath to OpenAPI integer enums:

Field CLI string OpenAPI integer Meaning
--item-type default 0 Normal message
--item-type thread 4 topic-style topic group
--item-type msg_thread 11 Normal group message thread
--flag-type message 2 Message-level bookmark (default)
--flag-type feed 1 feed level (sidebar bookmark)

⚠️ Reverse trap: Some third-party tutorials online (including some older lark-cli examples) have flag_type: 1=message / 2=feed written backwards. This project usesFeishu OpenAPI official ground truthas the standard (1=feed / 2=message, see the table above).list The command output also uses this same ground truth; when writing code to process the JSON, follow the table above.

list The command does not accept --flag-type/--item-type as an input parameter (list returns everything), it can only be filtered on the output flag_items[*].flag_type field. To see which bookmarks you have, just feishu-cli msg flag list -o json。

Supported combinations (the rest are rejected by the server):

  • default + message: message-level bookmark, the most common
  • thread + feed: topic-style topic group feed level
  • msg_thread + feed: normal group message thread feed level

Source code reference:internal/client/flag.go:23-28(authoritative for this repository, aligned with lark-cli shortcuts/im ItemType/FlagType definitions).