跳到主要内容

卡片消息

卡片消息用 msgType: "card" 发送,是机器人最常用的富交互形式。发出去之后还可以原地更新或撤回,无需重发新消息。

  • 主动发送:POST /api/open/v2/bot/messages
  • 回复入站消息:POST /api/open/v2/bot/messages/{messageId}/reply
  • 更新已发送卡片:PUT /api/open/v2/bot/messages/{messageId}/card
  • 删除已发送卡片:DELETE /api/open/v2/bot/messages/{messageId}/card

发送与回复所需 Scope 为 bot.message.send / bot.message.reply;更新与删除卡片所需 Scope 为 bot.message.send。

两种投递方式​

content 里二选一:

方式content 结构说明
内联卡片{"card": { ...卡片 JSON... }}卡片内容随消息一起提交,每次发送都要重复传
控制台模板{"cardId": "card_xxx"}引用控制台「卡片」页保存的模板,发送时才展开
  • 两个字段都缺时返回 400 + 400006,文案为 content.cardId不能为空。
  • cardId 指向的模板必须属于当前应用,否则返回 400 + 400006,文案为 卡片模板不存在。
  • 用 cardId 发送时,服务端会把模板内容展开后再校验,效果与内联一致。

卡片信封​

卡片 JSON 必须满足以下结构,否则发送时直接失败:

{
"schema": "2.0",
"body": {
"elements": []
}
}
字段要求不满足时的文案
schema必须为字符串 "2.0"仅支持schema为2.0的卡片
body必须为 JSON 对象card.body不能为空
body.elements必须为 JSON 数组card.body.elements必须为数组

以上字段之外的卡片属性(如用于样式的 header 等)服务端不解析、原样透传,能否渲染以客户端的卡片能力为准。

组件白名单​

elements 里的每个元素都必须是 JSON 对象,且 tag 必须在下列白名单内,否则返回 不支持的卡片组件: <你传的 tag>。

分组tag
文本plain_text、text、markdown、lark_md、text_tag、text_tag_list
布局与容器column_set、column、form、interactive_container、collapsible_panel、recycling_container
分隔hr、divider
图片与人员img、image、img_combination、icon、person、person_list
交互button、overflow、action、input、textarea、checkbox、radio、select_static、select_person、select_time、date_picker、picker_datetime、multi_select_static、multi_select_person
数据展示table、chart、note

上表的分组仅为便于查阅,服务端只做「tag 是否在白名单内」这一项判断。白名单只保证组件类型被接受,不校验每种组件的必填字段——例如一个缺 content 的 text 组件能通过服务端校验,但可能无法正常渲染。

嵌套容器​

容器组件通过以下四个字段之一挂载子元素,服务端会递归校验子元素:

字段服务端校验客户端渲染
elements✅✅
columns✅✅
children✅✅
actions✅❌ 不读

校验按 elements → columns → children → actions 的顺序查找,只取第一个命中的字段;如果同层级同时写了多个,后面的不会被校验。

⚠️ actions 是个坑,两端口径不一致。 服务端会校验 actions,客户端却从不读取它。所以按飞书官方写法写的 {"tag": "action", "actions": [...]} 能通过校验、接口返回成功,客户端却渲染成一张空卡片。

想让按钮组真的显示出来,子节点必须写进 elements:

{
"tag": "action",
"elements": [
{ "tag": "button", "text": { "tag": "plain_text", "content": "通过" } }
]
}

同理,其余容器(form、interactive_container、collapsible_panel、recycling_container)也只会从 elements 取子节点,children 只在客户端不认识的容器上兜底。

限制​

限制阈值超出时的文案
组件总数≤ 200卡片组件不能超过200个
卡片 JSON 体积≤ 64 KiB(UTF-8)卡片JSON不能超过64KiB
嵌套层级最深嵌套数组为第 9 层(body.elements 计第 1 层)卡片组件嵌套层级不能超过8

组件总数是递归统计的:容器本身与它的所有子孙节点各计 1。

嵌套层级的计数方式是「嵌套的数组层数」,不是组件个数,也不是容器个数。 body.elements 算第 1 层, 其下每出现一个子数组(elements/columns/children/actions)就再加一层,最多到第 9 层。

⚠️ 一个容易踩的坑:column_set → columns → column → elements 是两层数组, 所以「一层 column_set」实际消耗两层额度。 实测边界:只用 form(单层 elements)时嵌 8 层通过、9 层报错; 用 column_set/column 组合时叠 4 层通过、5 层报错。

链接字段限制​

以下四个字段只要出现,就必须是 http:// 或 https:// 开头的字符串,否则返回 卡片链接仅支持HTTP/HTTPS地址:

  • 元素自身的 url、img_url、src
  • action.url(元素 action 对象里的 url)

字段缺失(null)视为不校验。非字符串类型同样会被拒绝——例如把 url 写成数字或对象,报的是同一条文案。

这条校验发生在展开图片之前,所以图片组件依赖 img_key 而非 img_url 时(见下节)不会因为 img_url 缺失而报错。

图片组件​

img / image 组件在同时缺少 img_url 与 url 时,服务端会尝试用 img_key 解析出图片地址:

  1. 取 img_key;缺失则返回 card image img_key不能为空。
  2. 若 img_key 本身是 http:// / https:// 地址,直接作为 img_url 使用。
  3. 否则当作上传接口返回的 fileKey 解析(见 机器人快速接入 → 文件消息),解析成功且 MIME 类型以 image/ 开头时,把解析出的地址写入 img_url。

fileKey 的有效期约 10 分钟、且仅限上传它的应用使用,因此 img_key 必须在发送前临时上传获得,不能提前很久准备好。解析失败时分两种:

  • img_key 不是有效 fileKey(过期、跨应用、不存在)→ 403 + 400008,文案 fileKey无效或已过期;
  • img_key 指向的文件 MIME 类型不以 image/ 开头 → 400 + 400006,文案 card image img_key必须引用图片文件。

完整示例​

curl -X POST 'https://im.lanchr.com/api/open/v2/bot/messages' \
-H 'Content-Type: application/json' \
-H 'X-Open-App-Id: <app-id>' \
-H 'X-Open-Credential-Id: <credential-id>' \
-H 'X-Open-Timestamp: <timestamp-ms>' \
-H 'X-Open-Nonce: <unique-nonce>' \
-H 'X-Open-Signature: <hmac-sha256>' \
--data-binary '{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "order-notice-20260917-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{ "tag": "markdown", "content": "**订单号** SO-20260917\n**金额** ¥128.00" },
{ "tag": "hr" },
{
"tag": "column_set",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "button",
"text": { "tag": "plain_text", "content": "查看详情" },
"action": { "type": "url", "url": "https://example.com/orders/SO-20260917" }
}
]
}
]
}
]
}
}
}
}'

成功响应(code 为 "000000"):

{
"code": "000000",
"message": "成功",
"value": {
"messageId": 8479,
"msgSeq": 8703555237400514949,
"clientMessageId": "order-notice-20260917-0001",
"duplicate": false
},
"reqNo": "req_34dee87a15bd4ae8a135e99c22b6bb4b"
}

拿到 value.messageId 后即可用于后续的更新与删除。

示例​

下面每个示例都给出完整的请求体,替换 receiveId 就能直接调接口。截图取自蓝答桌面客户端的浅色主题,就是同一份 JSON 的实际渲染效果。

服务端只做结构校验,卡片长什么样完全由客户端决定。这些截图代表桌面客户端的渲染能力,换一个客户端可能长得不一样。校验规则见前面的组件白名单与限制。

每个示例下面的 ⚠️ 标注是实测踩过的坑——服务端会放行、客户端却不按预期渲染的情况,照着写之前先看一眼。

文本​

纯文本​

plain_text 渲染为一行普通文本,不支持任何标记语法。

纯文本示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-plain-text-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "plain_text",
"content": "服务将于今晚 23:00 进行例行维护,预计持续 30 分钟。"
}
]
}
}
}
}
  • plain_text 与 text 等价,text 是同一组件的别名。
  • 文案里的 **加粗**、[链接]() 等标记不会被解析,会原样显示。

Markdown 富文本​

markdown 支持加粗、斜体、链接、列表、行内代码等标记语法。

Markdown 富文本示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-markdown-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "markdown",
"content": "**订单号:** SO-20260922\n**金额:** ¥128.00\n\n- 商品:无线键盘\n- 数量:1\n- 收货人:张三"
}
]
}
}
}
}
  • markdown 与 lark_md 等价,客户端用同一套渲染器处理。
  • 换行用 \n;空行分段。HTML 标签会被转义,不会执行。

状态标签​

text_tag 渲染为一个小标签,用来标注级别或状态。

状态标签示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-text-tag-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "text_tag",
"text": {
"tag": "plain_text",
"content": "P1"
}
},
{
"tag": "plain_text",
"content": "数据库将在今晚进行维护。"
}
]
}
}
}
}
  • 标签文案写在 text.content 里;text_tag 是独立元素,会单独占一行,不会和后面的文本排在同一行。
  • 卡片标题栏里的 header.text_tag_list 是另一个字段,用来在标题右侧挂多个标签。

布局与容器​

多列并排​

column_set 包住多个 column,把内容横向并排成多列。

多列并排示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-column-set-0001",
"content": {
"card": {
"schema": "2.0",
"header": {
"title": {
"tag": "plain_text",
"content": "服务概览"
}
},
"body": {
"elements": [
{
"tag": "column_set",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "plain_text",
"content": "在线用户\n1,280"
}
]
},
{
"tag": "column",
"elements": [
{
"tag": "plain_text",
"content": "今日消息\n8,642"
}
]
},
{
"tag": "column",
"elements": [
{
"tag": "plain_text",
"content": "错误率\n0.02%"
}
]
}
]
}
]
}
}
}
}
  • column_set 的子节点必须放在 columns 里,不是 elements。
  • 每个 column 自己的子节点放在 column.elements 里。
  • ⚠️ 这一层结构会消耗两层嵌套额度:column_set → columns(第 1 层数组)→ column → elements(第 2 层数组),详见限制里的嵌套层级说明。

单列容器​

column 把多个组件归到同一列里,按垂直方向依次排列。

单列容器示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-column-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "column",
"elements": [
{
"tag": "plain_text",
"content": "版本 v2.6.0 已发布"
},
{
"tag": "markdown",
"content": "包含性能优化和安全修复。"
}
]
}
]
}
}
}
}
  • column 可以直接放在 body.elements 里,不必非得套在 column_set 内。
  • 效果上它和不用容器差别不大,主要价值是在 column_set 里划分列。

表单容器​

form 用来承载可提交的表单控件。

表单容器示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-form-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "form",
"elements": [
{
"tag": "plain_text",
"content": "表单内容(静态展示)"
}
]
}
]
}
}
}
}
  • form 的子节点放在 elements 里。
  • 客户端目前只做静态渲染,不会提交表单;表单控件的交互结果通过 feishu-card:action 事件抛出。

操作按钮组​

action 把多个按钮横向排列成一行。

操作按钮组示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-action-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "action",
"elements": [
{
"tag": "button",
"element_id": "approve",
"text": {
"tag": "plain_text",
"content": "通过"
},
"value": {
"action": "approve"
}
},
{
"tag": "button",
"element_id": "reject",
"text": {
"tag": "plain_text",
"content": "拒绝"
},
"value": {
"action": "reject"
}
}
]
}
]
}
}
}
}
  • ⚠️ 子节点必须写在 elements 里。服务端会依次查找 elements → columns → children → actions,所以写成飞书官方的 actions 也能通过校验,但客户端只读 elements,用 actions 会渲染成一张空卡片。

交互容器​

interactive_container 用来组合需要整体参与交互的内容。

交互容器示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-interactive-container-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "interactive_container",
"elements": [
{
"tag": "plain_text",
"content": "交互容器内容(未启用回调)"
}
]
}
]
}
}
}
}
  • 子节点放在 elements 里。

折叠面板​

collapsible_panel 把次要信息折叠起来,默认收起。

折叠面板示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-collapsible-panel-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "collapsible_panel",
"title": {
"tag": "plain_text",
"content": "更多信息"
},
"elements": [
{
"tag": "plain_text",
"content": "折叠面板内容"
}
]
}
]
}
}
}
}
  • 面板标题读 title(也兼容 header),子节点放在 elements 里。
  • ⚠️ 客户端不做折叠交互,标题和内容始终展开显示——想默认收起只能自己用 image 之类做视觉暗示。

循环容器​

recycling_container 用于按数据源循环渲染子项。

循环容器示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-recycling-container-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "recycling_container",
"elements": [
{
"tag": "plain_text",
"content": "循环项静态内容"
}
]
}
]
}
}
}
}
  • 子节点放在 elements 里。
  • 客户端目前只把子项平铺渲染一次,不执行循环或数据绑定。

分隔​

分割线​

hr 渲染为一条水平分割线,用来分隔上下文。

分割线示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-hr-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "plain_text",
"content": "维护窗口:2026-09-23 02:00 - 06:00"
},
{
"tag": "hr"
},
{
"tag": "plain_text",
"content": "维护期间消息推送可能延迟。"
}
]
}
}
}
}
  • hr 与 divider 等价。

图片与人员​

图片​

image 渲染一张图片,可以用 img_key 或 img_url 指定地址。

图片示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-image-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "image",
"img_key": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png",
"alt": {
"tag": "plain_text",
"content": "欢迎横幅"
}
}
]
}
}
}
}
  • img_url、url、img_key 三个字段客户端都会读,按这个顺序取第一个非空值。
  • img_key 给 http(s) 地址时直接当图片地址用;给 fileKey 时服务端会先解析(见图片组件)。
  • alt 是图片加载失败时的替代文本,不参与展示布局。

多图组合​

img_combination 把多张图片横向排成一组。

多图组合示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-img-combination-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "img_combination",
"img_list": [
{
"img_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png",
"alt": {
"tag": "plain_text",
"content": "报表一"
}
},
{
"img_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png",
"alt": {
"tag": "plain_text",
"content": "报表二"
}
},
{
"img_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png",
"alt": {
"tag": "plain_text",
"content": "报表三"
}
}
]
}
]
}
}
}
}
  • 图片列表放在 img_list 里,每项支持 img_url / url / img_key。
  • 图片地址为空或加载失败的项会退化成默认头像,不会留空位。

单个人员​

person 渲染一个人的头像和名字。

单个人员示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-person-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "person",
"user_id": "u_1001",
"name": {
"tag": "plain_text",
"content": "张三"
},
"avatar_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png"
}
]
}
}
}
}
  • 显示名依次读 name → user_name → user_id,所以 name 不传也能渲染出 user_id。
  • 头像读 avatar_url(也兼容 avatar);不传或地址非法时用默认头像。

人员列表​

person_list 横向排列多个人。

人员列表示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-person-list-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "person_list",
"persons": [
{
"user_id": "u_1002",
"name": {
"tag": "plain_text",
"content": "李四"
},
"avatar_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png"
},
{
"user_id": "u_1003",
"name": {
"tag": "plain_text",
"content": "王五"
},
"avatar_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png"
}
]
}
]
}
}
}
}
  • 人员数组放在 persons 里,每项字段与单个 person 相同。

图标​

icon 用来展示一个标准图标。

图标示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-icon-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "icon",
"token": "info_outlined"
}
]
}
}
}
}
  • ⚠️ 客户端目前没有图标字体,token 会被当纯文本原样显示(截图里显示的就是 info_outlined 字样)。想稳定呈现图标,建议改用 image 传图片。

交互​

链接按钮​

button 渲染为可点击按钮,带 url 时点击直接打开链接。

链接按钮示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-button-0001",
"content": {
"card": {
"schema": "2.0",
"header": {
"title": {
"tag": "plain_text",
"content": "✅ 构建成功"
}
},
"body": {
"elements": [
{
"tag": "markdown",
"content": "**分支:** master\n**提交:** abc123f\n**耗时:** 3m 24s"
},
{
"tag": "hr"
},
{
"tag": "plain_text",
"content": "测试覆盖率:92.5% | 代码扫描:通过"
},
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "查看构建详情"
},
"url": "https://ci.example.com/builds/1234"
}
]
}
}
}
}
  • 按钮文案读 text.content。
  • url 只接受 http:// / https://,否则发送时报 卡片链接仅支持HTTP/HTTPS地址。
  • 不带 url 时按钮走回调:客户端抛出 feishu-card:action 事件,value 原样带出。

表单控件​

input / textarea / checkbox / radio / select_static 渲染成可操作的控件。

表单控件示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-form-controls-0001",
"content": {
"card": {
"schema": "2.0",
"header": {
"title": {
"tag": "plain_text",
"content": "工单处理"
}
},
"body": {
"elements": [
{
"tag": "input",
"name": "assignee",
"placeholder": {
"tag": "plain_text",
"content": "处理人"
}
},
{
"tag": "select_static",
"name": "priority",
"placeholder": {
"tag": "plain_text",
"content": "优先级"
},
"options": [
{
"text": {
"tag": "plain_text",
"content": "高"
},
"value": "high"
},
{
"text": {
"tag": "plain_text",
"content": "中"
},
"value": "medium"
},
{
"text": {
"tag": "plain_text",
"content": "低"
},
"value": "low"
}
]
},
{
"tag": "textarea",
"name": "remark",
"placeholder": {
"tag": "plain_text",
"content": "处理说明"
}
},
{
"tag": "checkbox",
"name": "notify",
"label": {
"tag": "plain_text",
"content": "完成后通知我"
}
},
{
"tag": "radio",
"name": "close",
"label": {
"tag": "plain_text",
"content": "同时关闭工单"
}
}
]
}
}
}
}
  • 控件的提示文案依次读 label → placeholder → name,都取不到时显示「请选择」。
  • select_* / overflow / date_picker 等下拉类组件,客户端统一渲染成原生下拉框,选项读 options[].text。
  • ⚠️ checkbox / radio 的文案客户端读的是 label(不是飞书原生的 text),用 text 会显示成 name 或「请选择」。

数据展示​

表格​

table 渲染为只读表格。

表格示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-table-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "table",
"rows": [
[
"检查项",
"结果"
],
[
"单元测试",
"通过"
],
[
"代码扫描",
"通过"
]
]
}
]
}
}
}
}
  • rows 用二维数组,第一行会被当作表头。
  • 也支持 columns 定义列头,行数据可用对象形式;单元格里的对象会被取 content 显示。

图表​

chart 用来展示一组数值。

图表示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-chart-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "chart",
"title": {
"tag": "plain_text",
"content": "消息趋势"
},
"data": [
{
"name": "成功",
"value": 80
},
{
"name": "失败",
"value": 20
}
]
}
]
}
}
}
}
  • ⚠️ 客户端不执行图表脚本,只把 data 里的前 8 项渲染成横向数据条,value 被夹到 0–100 之间当作条宽。
  • data 也兼容 { values: [] } / { data: [] } / { series: [] } 等写法。

备注​

note 渲染为一行次要说明文字。

备注示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-note-0001",
"content": {
"card": {
"schema": "2.0",
"body": {
"elements": [
{
"tag": "note",
"content": "这是通知卡片的补充说明。"
}
]
}
}
}
}
  • note 的文案读 content(也兼容 text),样式上比正文更淡。

卡片级配置​

标题栏​

header 提供卡片标题、副标题、图标和标题标签。

标题栏示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-header-0001",
"content": {
"card": {
"schema": "2.0",
"config": {
"locales": [
"zh_cn"
]
},
"header": {
"title": {
"i18n": {
"zh_cn": "多语言通知"
}
},
"subtitle": {
"tag": "plain_text",
"content": "2026-09-22 09:00 生成"
},
"icon": {
"img_url": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png"
},
"text_tag_list": [
{
"text": {
"tag": "plain_text",
"content": "P1"
}
}
]
},
"body": {
"elements": [
{
"tag": "plain_text",
"content": "标题栏支持图标与多个标签。"
}
]
}
}
}
}
  • 标题读 title.content;也支持 title.i18n.{locale},此时要同时在 config.locales 里声明语言,客户端按顺序取第一个命中的。
  • subtitle 显示在标题下方。
  • icon 支持 img_url(图片)或 tag: "standard_icon" + token(标准图标)。
  • text_tag_list 是数组,会在标题右侧并排渲染多个标签。

宽度与摘要​

config 控制卡片宽度和会话列表里的摘要文案。

宽度与摘要示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-config-width-0001",
"content": {
"card": {
"schema": "2.0",
"config": {
"width_mode": "fill",
"summary": {
"content": "主分支构建成功 #1234"
}
},
"body": {
"elements": [
{
"tag": "plain_text",
"content": "width_mode 为 fill 时卡片占满可用宽度。"
}
]
}
}
}
}
  • width_mode 取值 default(min(400px, 68vw))、compact(同 default)、fill(min(600px, 100%))。
  • summary.content 是会话列表里显示的摘要,不传则用卡片正文。

居中显示​

config.horizontal_align 为 center 时卡片脱离气泡、在会话中居中占满整行。

居中显示示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-config-center-0001",
"content": {
"card": {
"schema": "2.0",
"config": {
"horizontal_align": "center",
"width_mode": "fill"
},
"header": {
"title": {
"tag": "plain_text",
"content": "消息助手通知"
}
},
"body": {
"elements": [
{
"tag": "plain_text",
"content": "这是一条居中显示的飞书卡片消息。"
}
]
}
}
}
}
  • 这是唯一会改变卡片整体布局的配置:命中后不再显示头像和发送者昵称,卡片自己占满一行。
  • 客户端依次检查 config.horizontal_align → config.align → align,任一为 center 即生效。
  • ⚠️ 布局变化只在客户端发生,服务端不解析这些字段,也不会因为写错而报错。

卡片级链接​

card_link 在卡片底部追加一个跳转入口。

卡片级链接示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-card-link-0001",
"content": {
"card": {
"schema": "2.0",
"card_link": {
"url": "https://status.example.com"
},
"body": {
"elements": [
{
"tag": "plain_text",
"content": "点击卡片链接查看完整状态页"
}
]
}
}
}
}
  • card_link.url 只接受 http:// / https://。
  • 它和元素级的 url 不是一回事:card_link 是卡片级字段,渲染在卡片底部,与 body.elements 无关。

综合示例​

系统维护公告​

标题 + 分割线 + Markdown 列表 + 链接按钮的常见组合。

系统维护公告示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-system-notice-0001",
"content": {
"card": {
"schema": "2.0",
"config": {
"summary": {
"content": "系统维护通知"
}
},
"header": {
"title": {
"tag": "plain_text",
"content": "🔧 系统维护通知"
}
},
"body": {
"elements": [
{
"tag": "plain_text",
"content": "维护时间:2026年8月30日 02:00 - 06:00(约4小时)"
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "**维护内容:**\n- 数据库性能优化\n- 消息推送服务升级\n- 安全补丁更新"
},
{
"tag": "plain_text",
"content": "维护期间部分功能可能暂时不可用,敬请谅解。"
},
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "查看详情"
},
"url": "https://status.example.com/maintenance/20260830"
}
]
}
}
}
}

入群欢迎​

带图片的欢迎卡片,适合机器人进群后的第一条消息。

入群欢迎示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-welcome-0001",
"content": {
"card": {
"schema": "2.0",
"config": {
"summary": {
"content": "欢迎加入团队!"
}
},
"header": {
"title": {
"tag": "plain_text",
"content": "🎉 欢迎加入"
}
},
"body": {
"elements": [
{
"tag": "markdown",
"content": "欢迎来到我们的团队!\n\n为了让您快速上手,我们准备了以下资源:"
},
{
"tag": "hr"
},
{
"tag": "plain_text",
"content": "📘 新人指南 | 💬 团队规范 | 🛠️ 开发文档"
},
{
"tag": "image",
"img_key": "https://limp.oss-cn-hangzhou.aliyuncs.com/resources/avatar/avatar10.png",
"alt": {
"tag": "plain_text",
"content": "欢迎横幅"
}
},
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "查看新人指南"
},
"url": "https://wiki.example.com/onboarding"
}
]
}
}
}
}

审批通知​

用 column_set 排出「字段 / 值」两列,再配一行操作按钮。

审批通知示例在会话中的渲染效果

{
"receiveIdType": "group_id",
"receiveId": "<群ID>",
"msgType": "card",
"clientMessageId": "card-approval-0001",
"content": {
"card": {
"schema": "2.0",
"config": {
"summary": {
"content": "请假申请待审批"
}
},
"header": {
"title": {
"tag": "plain_text",
"content": "📋 请假申请待审批"
}
},
"body": {
"elements": [
{
"tag": "column_set",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "plain_text",
"content": "申请人"
},
{
"tag": "plain_text",
"content": "请假类型"
},
{
"tag": "plain_text",
"content": "请假时长"
}
]
},
{
"tag": "column",
"elements": [
{
"tag": "plain_text",
"content": "张三"
},
{
"tag": "plain_text",
"content": "年假"
},
{
"tag": "plain_text",
"content": "2026-09-24 ~ 2026-09-25(2 天)"
}
]
}
]
},
{
"tag": "hr"
},
{
"tag": "note",
"content": "提交时间:2026-09-22 09:12"
},
{
"tag": "action",
"elements": [
{
"tag": "button",
"element_id": "approve",
"text": {
"tag": "plain_text",
"content": "通过"
},
"value": {
"action": "approve",
"requestId": "LV-20260922-001"
}
},
{
"tag": "button",
"element_id": "reject",
"text": {
"tag": "plain_text",
"content": "拒绝"
},
"value": {
"action": "reject",
"requestId": "LV-20260922-001"
}
}
]
}
]
}
}
}
}

更新已发送的卡片​

PUT /api/open/v2/bot/messages/{messageId}/card

请求体:

字段类型必填说明
cardobject是新的完整卡片 JSON,校验规则与发送时相同
versioninteger否乐观锁版本号;省略时不校验版本

只有本应用发出的卡片消息(direction=OUTBOUND)才能更新,否则返回 403 + 400008,文案为 消息未投递给此应用;目标不是卡片消息时返回 400 + 400006,文案为 卡片消息不存在。

版本号语义:

  • 卡片的当前版本记录在消息的扩展信息里。内联卡片(content.card)初始版本为 1;用模板(content.cardId)发送时,初始版本为该模板当时的版本号。
  • 每次成功更新后版本号 +1。
  • 请求带了 version 且与当前版本不一致时,更新会被拒绝。
  • 不带 version 时不做版本校验,直接覆盖,适合「每次都是全量重算卡片」的场景。
  • 并发更新、或「读取 → 展示给用户 → 用户点击提交」这类跨请求的场景,建议始终带上 version。

⚠️ 版本冲突返回 HTTP 500 + code 500000,且响应文案是通用的「服务内部错误,请稍后重试」,不是 409,也拿不到「版本过期」这类提示。客户端无法从响应区分「版本冲突」与真正的平台内部错误。做乐观锁时请这样处理:收到 500000 后重新读取卡片当前状态再决定是否重试,不要直接向用户报「系统故障」。

更新成功后返回与发送相同结构的响应,但 clientMessageId 固定为 null、duplicate 固定为 false:

{
"code": "000000",
"message": "成功",
"value": {
"messageId": 8479,
"msgSeq": 8703555237400514949,
"clientMessageId": null,
"duplicate": false
},
"reqNo": "req_60c3253bf8294be18de23fb21b017fec"
}

更新会实时同步到已收到该消息的会话成员。

删除已发送的卡片​

DELETE /api/open/v2/bot/messages/{messageId}/card

无请求体。删除即撤回该卡片,返回 value: true;若撤回未生效返回 value: false。前置校验与更新一致(必须是本应用发出的卡片消息,否则 403 + 400008 / 400 + 400006)。

⚠️ 服务端不记录撤回状态:对已删除的卡片再次调用 PUT .../card 仍会返回成功,而不会报错。如果业务上「撤回」是终态,请在应用侧自行拦截,不要依赖服务端拒绝。

相关错误文案​

文案HTTPcode
仅支持schema为2.0的卡片400400006
card.body不能为空400400006
card.body.elements必须为数组400400006
卡片组件必须为对象400400006
不支持的卡片组件: xxx400400006
卡片组件不能超过200个400400006
卡片JSON不能超过64KiB400400006
卡片组件嵌套层级不能超过8400400006
卡片链接仅支持HTTP/HTTPS地址400400006
content.cardId不能为空400400006
卡片模板不存在400400006
card image img_key不能为空400400006
card image img_key必须引用图片文件400400006
卡片消息不存在400400006
fileKey无效或已过期403400008
消息未投递给此应用403400008
服务内部错误,请稍后重试(版本冲突时)500500000

上表除最后一行外,文案都会原样返回在 message 字段里。版本冲突是唯一的例外:它走通用错误兜底,message 只有 服务内部错误,请稍后重试,看不出真实原因。

错误响应体统一为 ApiResult 信封,详见鉴权与签名 → 错误响应契约。