All skills
breath57 avatar

/dingtalk-message

@9324c92
by breath57breath57/dingtalk-skills101 stars
9

钉钉消息发送。当用户提到"钉钉消息"、"发消息"、"发通知"、"群通知"、"群消息"、"Webhook"、"机器人消息"、"机器人发消息"、"工作通知"、"单聊消息"、"群聊消息"、"撤回消息"、"消息已读"、"发送Markdown"、"发卡片消息"、"ActionCard"、"@某人"、"@员工"、"at某人"、"提醒某人"、"dingtalk message"、"send message"、"robot message"、"work notification"时使用此技能。支持:群自定义 Webhook 机器人(文本/Markdown/ActionCard/Link/FeedCard + 加签 + @某人)、企业内部应用机器人单聊和群聊发送、消息撤回、已读查询、工作通知等全部消息类操作。

Use this Skill: https://skilld.dev/gh/breath57/dingtalk-skills/dingtalk-message

This session only. Nothing lands on disk.

referencesapi.md

≈5.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

钉钉消息 API 参考

本文档覆盖三类消息发送方式:群自定义 Webhook 机器人、企业内部应用机器人、工作通知。


一、群自定义 Webhook 机器人

基础地址:群设置中获取的 Webhook URL
认证:无需 accessToken,URL 中自带 access_token 参数
限制:每个机器人每分钟最多 20 条消息


发送消息

POST https://oapi.dingtalk.com/robot/send?access_token=<WEBHOOK_TOKEN>
Content-Type: application/json

若配置了加签安全模式,还需附加 &timestamp=<ms>&sign=<签名>


文本消息
{
  "msgtype": "text",
  "text": {
    "content": "项目 v2.1 已上线,请验收。"
  },
  "at": {
    "atMobiles": ["138xxxx1234"],
    "atUserIds": ["user123"],
    "isAtAll": false
  }
}
字段 类型 必填 说明
msgtype string ✅ 固定 "text"
text.content string ✅ 消息内容
at.atMobiles string[] ❌ 按手机号 @ 人
at.atUserIds string[] ❌ 按 userId @ 人
at.isAtAll boolean ❌ 是否 @所有人

返回示例:

{ "errcode": 0, "errmsg": "ok" }

Markdown 消息
{
  "msgtype": "markdown",
  "markdown": {
    "title": "部署通知",
    "text": "## v2.1 部署完成\n\n- **环境**:production\n- **时间**:2026-03-10 15:00\n- **状态**:✅ 成功"
  },
  "at": {
    "isAtAll": false
  }
}
字段 类型 必填 说明
markdown.title string ✅ 推送通知展示标题
markdown.text string ✅ Markdown 正文

支持的 Markdown 语法:标题(#~######)、加粗、链接、图片、有序/无序列表、引用。


ActionCard(整体跳转)
{
  "msgtype": "actionCard",
  "actionCard": {
    "title": "技术评审邀请",
    "text": "## 技术评审\n\n请参加明天 14:00 的架构评审会议",
    "singleTitle": "查看详情",
    "singleURL": "https://example.com/review"
  }
}
字段 类型 必填 说明
actionCard.title string ✅ 消息标题
actionCard.text string ✅ Markdown 正文
actionCard.singleTitle string ✅ 单按钮文字
actionCard.singleURL string ✅ 按钮跳转链接

ActionCard(多按钮)
{
  "msgtype": "actionCard",
  "actionCard": {
    "title": "选择操作",
    "text": "## 审批请求\n\n张三提交了报销申请",
    "btnOrientation": "0",
    "btns": [
      { "title": "同意", "actionURL": "https://example.com/approve" },
      { "title": "拒绝", "actionURL": "https://example.com/reject" }
    ]
  }
}
字段 类型 必填 说明
actionCard.btnOrientation string ❌ "0" 竖排(默认)、"1" 横排
actionCard.btns array ✅ 按钮列表
btns[].title string ✅ 按钮文字
btns[].actionURL string ✅ 按钮跳转链接

Link 消息
{
  "msgtype": "link",
  "link": {
    "title": "版本发布公告",
    "text": "v2.1 正式发布,包含性能优化和安全修复。",
    "messageUrl": "https://example.com/release",
    "picUrl": "https://example.com/logo.png"
  }
}
字段 类型 必填 说明
link.title string ✅ 消息标题
link.text string ✅ 消息摘要
link.messageUrl string ✅ 点击跳转链接
link.picUrl string ❌ 缩略图 URL

FeedCard 消息
{
  "msgtype": "feedCard",
  "feedCard": {
    "links": [
      {
        "title": "需求评审会议纪要",
        "messageURL": "https://example.com/doc/1",
        "picURL": "https://example.com/img1.png"
      },
      {
        "title": "技术方案设计文档",
        "messageURL": "https://example.com/doc/2",
        "picURL": "https://example.com/img2.png"
      }
    ]
  }
}
字段 类型 必填 说明
feedCard.links array ✅ FeedCard 链接列表
links[].title string ✅ 单条标题
links[].messageURL string ✅ 点击跳转链接
links[].picURL string ❌ 缩略图 URL

加签计算(HMAC-SHA256)

适用于配置了"加签"安全模式的自定义机器人。开启加签后,所有请求都必须带签名,否则返回 310000 sign not match。

签名算法:

  1. timestamp = 当前毫秒级时间戳
  2. string_to_sign = timestamp + "\n" + secret
  3. sign = URL-Safe Base64 编码(HMAC-SHA256(secret, string_to_sign)的二进制结果)

最终请求 URL:

https://oapi.dingtalk.com/robot/send?access_token=<TOKEN>&timestamp=<timestamp>&sign=<sign>

timestamp 与钉钉服务器时差不能超过 1 小时,否则签名验证失败。


二、企业内部应用机器人

基础地址:https://api.dingtalk.com/v1.0/robot
认证:请求头 x-acs-dingtalk-access-token: <accessToken>

robotCode 等于应用的 appKey(完全一致)。

钉钉身份标识体系

钉钉有三种用户 ID,使用场景各不相同:

标识 说明 作用域 消息 API 支持
userId(也叫 staffId) 企业内部的用户 ID 单个企业内唯一 ✅ 机器人和工作通知 API 均只接受此 ID
unionId 跨企业/跨应用的用户 ID 同一法人跨组织唯一 ❌ 不能直接用于发送消息
openId 第三方应用作用域的用户 ID 单个第三方应用内唯一 企业内部应用不涉及

重要:所有消息发送 API(机器人单聊、群聊、工作通知)均只接受 userId,传入 unionId 会被判定为无效用户。

userId 获取方式
  1. 机器人回调(最常用):消息体中 senderStaffId 字段即为发送者的 userId
  2. unionId → userId 转换:POST /topapi/user/getbyunionid?access_token=<旧版token>,body: {"unionid": "<unionId>"}
  3. 钉钉管理后台:PC 端钉钉 → 工作台 → 管理后台 → 通讯录 → 成员详情
  4. API 查询:POST /topapi/v2/user/getbymobile(按手机号查),或遍历部门成员
userId ↔ unionId 互转
方向 API 请求 body 返回值
userId → unionId POST /topapi/v2/user/get?access_token=<旧版token> {"userid": "<userId>"} result.unionid(注意:无下划线的 unionid 有值,union_id 可能为空)
unionId → userId POST /topapi/user/getbyunionid?access_token=<旧版token> {"unionid": "<unionId>"} result.userid
openConversationId 获取方式

群聊 API 需要 openConversationId(以 cid 开头),获取方式:

  1. 机器人回调(推荐):在群中 @机器人,回调消息体的 conversationId 字段即为该值
  2. 调用 IM 接口创建群时返回

回调中 conversationType: "2" 表示群聊,"1" 表示单聊


批量发送单聊消息

POST /v1.0/robot/oToMessages/batchSend
Content-Type: application/json

{
  "robotCode": "<appKey>",
  "userIds": ["user001", "user002"],
  "msgKey": "sampleText",
  "msgParam": "{\"content\": \"你好,这是机器人消息\"}"
}
字段 类型 必填 说明
robotCode string ✅ 机器人 robotCode,通常等于 appKey
userIds string[] ✅ 用户 staffId(userId)列表,最多 20 个
msgKey string ✅ 消息类型 key(见下方消息类型表)
msgParam string ✅ 消息参数 JSON 字符串

返回示例:

{
  "processQueryKey": "abc123",
  "invalidStaffIdList": [],
  "flowControlledStaffIdList": []
}

发送群聊消息

POST /v1.0/robot/groupMessages/send
Content-Type: application/json

{
  "robotCode": "<appKey>",
  "openConversationId": "<群 openConversationId>",
  "msgKey": "sampleText",
  "msgParam": "{\"content\": \"群通知:明天 10:00 开周会\"}"
}
字段 类型 必填 说明
robotCode string ✅ 机器人 robotCode
openConversationId string ✅ 群会话 ID
msgKey string ✅ 消息类型 key
msgParam string ✅ 消息参数 JSON 字符串

返回:{ "processQueryKey": "xxx" }


查询单聊消息已读状态

GET /v1.0/robot/oToMessages/readStatus?robotCode=<appKey>&processQueryKey=<processQueryKey>

注意:此接口为 GET 方法,参数通过 query string 传递,不是 POST body。

返回示例:

{
  "messageReadInfoList": [
    {
      "name": "张三",
      "userId": "user001",
      "readStatus": "read",
      "readTimestamp": 1710000000000
    }
  ]
}
字段 类型 说明
readStatus string read(已读)或 unread(未读)
readTimestamp long 阅读时间戳(毫秒),未读时为 0

撤回单聊消息

POST /v1.0/robot/otoMessages/batchRecall
Content-Type: application/json

{
  "robotCode": "<appKey>",
  "processQueryKeys": ["<processQueryKey1>"]
}

返回示例:

{
  "successResult": ["processQueryKey1"],
  "failedResult": {}
}

撤回群聊消息

POST /v1.0/robot/groupMessages/recall
Content-Type: application/json

{
  "robotCode": "<appKey>",
  "openConversationId": "<openConversationId>",
  "processQueryKeys": ["<processQueryKey>"]
}

返回示例:

{
  "successResult": ["processQueryKey"],
  "failedResult": {}
}

消息类型(msgKey & msgParam)

msgKey 类型 msgParam 示例
sampleText 文本 {"content": "消息内容"}
sampleMarkdown Markdown {"title": "标题", "text": "# 正文\n内容"}
sampleActionCard ActionCard(整体跳转) {"title": "标题", "text": "正文", "singleTitle": "按钮", "singleURL": "https://..."}
sampleActionCard2 ActionCard(按钮竖排) {"title": "标题", "text": "正文", "actionTitle1": "按钮1", "actionURL1": "url1", "actionTitle2": "按钮2", "actionURL2": "url2"}
sampleActionCard3 ActionCard(按钮横排) 同上
sampleLink 链接 {"title": "标题", "text": "描述", "messageUrl": "url", "picUrl": "图片url"}
sampleImageMsg 图片 {"photoURL": "https://..."}
sampleAudio 语音 {"mediaId": "xxx", "duration": "3000"}

msgParam 必须是 JSON 字符串,而非 JSON 对象。


三、工作通知

基础地址:https://oapi.dingtalk.com
认证:查询参数 access_token=<旧版 access_token>(通过 /gettoken 获取)


发送工作通知

POST /topapi/message/corpconversation/asyncsend_v2?access_token=<token>
Content-Type: application/json

{
  "agent_id": "<agentId>",
  "userid_list": "user001,user002",
  "to_all_user": false,
  "msg": {
    "msgtype": "text",
    "text": { "content": "你的报销申请已审批通过" }
  }
}
字段 类型 必填 说明
agent_id long ✅ 应用 agentId
userid_list string 条件 逗号分隔的 userId(最多 100 个),与 dept_id_list 二选一
dept_id_list string 条件 逗号分隔的部门 ID
to_all_user boolean ❌ 是否推送全员(true 时忽略 userid_list/dept_id_list)
msg object ✅ 消息体

返回示例:

{ "errcode": 0, "errmsg": "ok", "task_id": 123456 }

工作通知消息类型

文本:

{ "msgtype": "text", "text": { "content": "消息内容" } }

Markdown:

{ "msgtype": "markdown", "markdown": { "title": "标题", "text": "## 正文\n内容" } }

ActionCard(整体跳转):

{
  "msgtype": "action_card",
  "action_card": {
    "title": "标题",
    "markdown": "## 正文内容",
    "single_title": "查看详情",
    "single_url": "https://example.com"
  }
}

ActionCard(多按钮):

{
  "msgtype": "action_card",
  "action_card": {
    "title": "标题",
    "markdown": "## 正文内容",
    "btn_orientation": "0",
    "btn_json_list": [
      { "title": "同意", "action_url": "https://example.com/approve" },
      { "title": "拒绝", "action_url": "https://example.com/reject" }
    ]
  }
}

OA 消息:

{
  "msgtype": "oa",
  "oa": {
    "message_url": "https://example.com/detail",
    "head": { "bgcolor": "FFBBBBBB", "text": "正文标题" },
    "body": {
      "title": "报销审批",
      "form": [
        { "key": "申请人", "value": "张三" },
        { "key": "金额", "value": "¥3,200" }
      ],
      "content": "请尽快处理"
    }
  }
}

查询工作通知发送结果

POST /topapi/message/corpconversation/getsendresult?access_token=<token>
Content-Type: application/json

{
  "agent_id": "<agentId>",
  "task_id": 123456
}

返回示例:

{
  "errcode": 0,
  "send_result": {
    "invalid_user_id_list": [],
    "forbidden_user_id_list": [],
    "read_user_id_list": ["user001"],
    "unread_user_id_list": ["user002"],
    "failed_user_id_list": []
  }
}

撤回工作通知

POST /topapi/message/corpconversation/recall?access_token=<token>
Content-Type: application/json

{
  "agent_id": "<agentId>",
  "msg_task_id": 123456
}

返回:{ "errcode": 0, "errmsg": "ok" }


四、sessionWebhook(回调临时回复通道)

当机器人收到用户消息回调时,消息体中包含 sessionWebhook 字段。这是一个临时 Webhook URL,可直接 POST 回复消息,无需 accessToken。

回调消息体关键字段

{
  "conversationId": "cidXXXXX==",
  "conversationType": "2",
  "senderStaffId": "25262904",
  "senderUnionId": "K1mxiiGFgkVfWYR5tNM04lAiEiE",
  "senderNick": "张三",
  "senderCorpId": "dingxxxxx",
  "robotCode": "dingxxxxxx",
  "text": { "content": " 用户发送的内容" },
  "msgtype": "text",
  "atUsers": [
    { "dingtalkId": "xxx", "staffId": "25262904", "unionId": "K1mxiiGFgkVfWYR5tNM04lAiEiE" }
  ],
  "sessionWebhook": "https://oapi.dingtalk.com/robot/sendBySession?session=xxxxx",
  "sessionWebhookExpiredTime": 1773216797267
}
字段 说明
conversationId 会话 ID,群聊时即 openConversationId
conversationType "1" 单聊、"2" 群聊
senderStaffId 发送者的 userId(企业内部群始终存在;外部群中的外部用户可能为空)
senderUnionId 发送者的 unionId(跨组织通用,始终存在)
senderCorpId 发送者所在企业的 corpId
atUsers[].staffId 被@用户的 userId(外部群中的外部用户此字段为空)
atUsers[].unionId 被@用户的 unionId(始终存在)
robotCode 机器人 robotCode(= appKey)
sessionWebhook 临时回复 URL(约 1.5 小时有效)
sessionWebhookExpiredTime 过期时间戳(毫秒)

通过 sessionWebhook 回复

POST <sessionWebhook>
Content-Type: application/json

{
  "msgtype": "text",
  "text": { "content": "收到,正在处理..." }
}

支持所有 Webhook 消息格式(text/markdown/actionCard/link/feedCard),返回:

{ "errcode": 0, "errmsg": "ok" }

无需加签、无需 accessToken,是机器人回复消息最简单的方式。


场景路由(收到用户请求后的判断逻辑)

用户想发消息
├─ 发到群里?
│  ├─ 通用群消息(含 @某人)→ 询问用户选择方式(见「群消息发送方式选择」)
│  ├─ 明确需要撤回或查已读 → 企业机器人群聊
│  └─ 正在处理机器人回调,直接回复 → sessionWebhook
├─ 发给个人?
│  ├─ 以机器人身份发私信 → 企业机器人单聊
│  └─ 以应用身份推工作通知 → 工作通知
├─ 撤回/查已读?
│  ├─ 机器人消息 → 企业机器人的撤回/已读 API
│  └─ 工作通知 → 工作通知的查询/撤回 API
└─ 回复机器人收到的消息? → sessionWebhook

群消息发送方式选择

用户发起群消息请求时,必须先询问选择哪种方式:

方式 需要提供 如何获取 说明
Webhook 机器人 WEBHOOK_URL 群设置 → 智能群助手 → 添加自定义机器人 → 复制 URL 无需应用权限,配置最简单;支持 @某人(at.atUserIds)
企业内部应用机器人 openConversationId(群会话 ID) 机器人收到群消息时,回调体的 conversationId 字段即为该值 需要 APP_KEY/APP_SECRET;支持撤回、查已读

推荐 Webhook,只需一个 URL 即可,无需任何应用权限。

  • 选 Webhook:收集 DINGTALK_WEBHOOK_URL(若启用加签还需 DINGTALK_WEBHOOK_SECRET),持久化后执行
  • 选 企业机器人:收集 openConversationId,复用已有的 APP_KEY/APP_SECRET,调用 groupMessages/send

身份标识

所有消息发送 API 均只接受 userId(staffId),不接受 unionId。

标识 作用域 能否用于发消息
userId(= staffId) 单个企业内唯一 ✅ 唯一接受的 ID
unionId 跨组织唯一 ❌ 会被判定无效用户

userId 获取方式:

  1. 机器人回调:消息体 senderStaffId 字段
  2. unionId → userId:bash scripts/dt_helper.sh --to-userid <unionId>
  3. 手机号 → userId:POST /topapi/v2/user/getbymobile
  4. 管理后台:PC 端钉钉 → 工作台 → 管理后台 → 通讯录

回调消息中的身份字段:

字段 含义 可靠性
senderStaffId 发送者 userId 企业内部群始终存在;外部群中外部用户可能为空
senderUnionId 发送者 unionId 始终存在

注意 result.unionid(无下划线)有值,result.union_id(有下划线)在部分企业中为空。


消息类型速查

Webhook 消息类型

直接在请求 body 的 msgtype 字段指定:text | markdown | actionCard | link | feedCard

机器人消息类型

通过 msgKey + msgParam(JSON 字符串)指定:

msgKey 类型 msgParam 关键字段
sampleText 文本 content
sampleMarkdown Markdown title, text
sampleActionCard ActionCard title, text, singleTitle, singleURL
sampleLink 链接 title, text, messageUrl, picUrl
sampleImageMsg 图片 photoURL

重要:msgParam 必须是 JSON 字符串,不是对象。

工作通知消息类型

在 msg 对象的 msgtype 字段指定:text | markdown | action_card

注意工作通知的 action_card 用下划线(不同于 Webhook 的 actionCard)。


错误码

Webhook 错误码

errcode 说明 处理建议
0 成功 —
310000 keywords not in content 消息内容须包含自定义关键词
310000 sign not match 检查签名计算或 timestamp 是否过期(±1h)
310000 token is not exist Webhook URL 无效或已被删除
302033 send too fast 超过 20 条/分钟限制,等待后重试

机器人错误码

错误 说明 处理建议
401 accessToken 过期 重新获取
403 权限不足 开通 Robot.Message.Send 等权限
invalidStaffIdList 非空 userId 无效 确认用户在组织内
flowControlledStaffIdList 非空 被限流 稍候重试

工作通知错误码

errcode 说明 处理建议
0 成功 —
33 access_token 过期 重新调用 /gettoken
40035 参数不合法 检查 agent_id、userid_list 格式
88 agent_id 不存在 确认应用 agentId
90018 userid_list 超过 100 分批发送

所需应用权限

功能 权限
机器人单聊消息 Robot.Message.Send
机器人群聊消息 Robot.GroupMessage.Send
消息已读查询 Robot.Message.Query
消息撤回 Robot.Message.Recall
工作通知发送 Message.CorpConversation.AsyncSend
工作通知撤回 Message.CorpConversation.Recall
Webhook 无需应用权限
sessionWebhook 无需应用权限(回调消息自带)

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides a comprehensive interface for sending messages via DingTalk's various channels (Webhook, Internal App, Work Notifications). It manages credentials through a local configuration file, handles token refreshing, and includes specific instructions for identity conversion and message formatting.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    1/2 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 9324c92. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 6 months ago
  • API
  • dingtalk
  • webhook
  • notifications
  • messaging
  • robot
  • chinese
  • enterprise

README badge

README badge for breath57/dingtalk-skills/dingtalk-message

Sends messages through DingTalk via four channels: Webhook robots (group notifications), enterprise app robots (direct messages and group chat with recall and read receipts), work notifications (application pushes), and sessionWebhook (callback replies). Handles text, Markdown, ActionCard, Link, and FeedCard message types with optional signature verification and @mentions.

Generated from the current SKILL.md.

Does this skill support sending messages to groups?
Yes. It supports group messages via Webhook robots (simplest, no token needed) or enterprise internal app robots. You must choose which method when sending to a group.
What message types can I send?
Text, Markdown, ActionCard, Link, and FeedCard. The skill also supports message recall, read status queries, and @mentions of specific users.
Do I need to configure tokens upfront?
No. The skill prompts you to provide configuration (Webhook URL, app key/secret, etc.) on first use and stores it. Webhook messages require no token; robot and work notification messages fetch tokens on demand.
Can I send private messages (DM) to individual users?
Yes, using enterprise internal app robots. The skill converts unionId to userId automatically and supports single-chat message sending.
What if a message fails to send?
Check the HTTP status and error code in the response. The skill references an errors section in references/api.md that maps Dingtalk error codes to causes and solutions.

Generated from the current SKILL.md. These answers refresh after source changes.