大多数情况下,这是因为安全设置(关键词或签名)不匹配,或者触发了频率限制,导致接口返回 HTTP 200 但业务逻辑失败。
先说结论:接口请求成功不代表消息送达,需优先检查返回 body 中的 errcode 及群机器人安全设置。
- 先确认:接口响应 body 中 errcode 是否为 0
- 先处理:核对 webhook 地址与安全设置(关键词/签名)
- 再验证:手动发送测试消息确认群内显示
命令速用版
使用 curl 直接测试 webhook,避免代码封装掩盖真实响应:
curl 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"msgtype":"text","text":{"content":"测试消息"}}'执行后直接观察终端输出的 JSON 响应,重点看 errcode 字段。
为什么会这样
很多开发者误以为 HTTP 状态码 200 就代表消息发送成功。实际上,钉钉开放平台接口在请求合法到达服务器时就会返回 200,但业务是否成功取决于返回 body 中的 errcode。
常见原因包括:
- 安全设置不匹配:群机器人开启了关键词过滤或签名验证,但请求内容未包含关键词或签名计算错误。
- 频率限制:短时间内发送过多消息,触发官方频率控制策略。
- 机器人状态异常:机器人已被移除群聊或被管理员禁用,但 webhook 地址未变。
- 内容过滤:消息内容包含敏感词,被系统拦截。
分步处理
1. 检查接口响应体
不要只判断 HTTP 状态码。确保你的代码解析了响应 JSON,并判断 errcode 是否为 0。如果 errcode 不为 0,根据 errmsg 提示修改。
2. 核对安全设置
登录钉钉群设置,找到机器人详情,检查安全设置:
- 关键词:确保发送内容中完整包含设置的关键词(区分大小写)。
- 签名:如果开启了签名,必须按文档要求计算 timestamp 和 sign,并拼接到 access_token 后。
- IP 白名单:如果设置了白名单,确保服务器出口 IP 在列表内。
以下是常见语言的签名计算示例(secret 为机器人安全设置中的密钥):
Python 示例:
import time, hmac, hashlib, base64, urllib.parse
secret = 'SEC...'
timestamp = str(round(time.time() * 1000))
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
webhook = 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN×tamp={}&sign={}'.format(timestamp, sign)Java 示例:
Long timestamp = System.currentTimeMillis();
String secret = "SEC...";
String stringToSign = timestamp + "\n" + secret;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
String webhook = "https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN×tamp=" + timestamp + "&sign=" + sign;Node.js 示例:
const crypto = require('crypto');
const secret = 'SEC...';
const timestamp = Date.now().toString();
const stringToSign = timestamp + '\n' + secret;
const sign = crypto.createHmac('sha256', secret).update(stringToSign).digest('base64');
const encodedSign = encodeURIComponent(sign);
const webhook = `https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN×tamp=${timestamp}&sign=${encodedSign}`;3. 检查发送频率
如果 errcode 提示频率限制,需要降低发送频次。官方文档通常建议控制在每分钟 20 条以内,具体请以最新文档为准,建议增加重试机制和间隔。
4. 确认机器人状态
在群内查看机器人是否在成员列表中。如果被移除,需要重新添加并获取新的 webhook 地址。
怎么验证是否生效
完成上述调整后,使用命令速用版中的 curl 命令发送一条纯文本测试消息。
验证标准:
- curl 返回 errcode 为 0。
- 钉钉群聊界面立即出现该条测试消息。
- 消息内容与发送内容一致,无乱码或缺失。
常见坑
- 签名计算错误:签名需要使用 HMAC-SHA256 算法,且 timestamp 单位为毫秒,拼接格式必须严格符合文档要求。
- 关键词匹配:关键词是包含关系,但必须完全匹配,例如设置“报警”,内容含“报警通知”可以,但“警”不行。
- HTTPS 证书:部分服务器环境请求 HTTPS 接口时未验证证书导致请求被拦截,确保 curl 或代码中 SSL 验证正常。
- 复制错误:webhook 地址中的 access_token 容易复制不全或混入空格,导致认证失败。
参考来源
- 钉钉开放平台,自定义机器人接入,https://open.dingtalk.com/document/orgapp/custom-robot-access
- 钉钉开放平台,机器人消息发送频率说明,https://open.dingtalk.com/document/orgapp/custom-bot-send-message-type