资讯动态

企业微信外部群消息推送:Webhook与客户群群发API实战解析

发布时间:2026/10/20 20:00:37 来源:尧图企业网站定制
上个月接了个需求客户下单后要往对应的客户服务群里自动推一条进度通知。当时我第一反应是去找企业微信“发送应用消息”接口心想这不就是一条消息的事。结果翻了半天文档被一句话堵了回来应用消息只能发给企业内部成员根本触达不了外部联系人。这里的“外部群”指的是包含微信用户、企业外合作方成员的群。这类群里的人不在你的企业通讯录里不能靠 userid、部门 id 寻址。后来我实际跑下来发现真正适合做外部群主动推送的主要就是两条路群机器人 Webhook以及客户群群发 API。这篇就把这两种方案的差异、调用方法和踩过的坑一次说清楚。1. 先搞清楚“外部群主动发消息”到底卡在哪很多第一次做企业微信开发的人和我一开始的直觉一样既然内部群能通过应用消息推那外部群也应该有个类似的接口直接指定群 ID 发一条消息就行。实际上没有这么简单。企业微信的安全设计里外部联系人属于“半开放”对象。你可以读取客户群的基本信息、群成员列表也可以创建群发任务但不能像内部消息那样随心所欲地直接推给外部客户。原因也不难理解如果任何开发者都能拿到群 ID 就向群里塞消息外部群马上会变成广告重灾区微信生态对“骚扰触达”的容忍度非常低。所以“外部群主动发消息”这个需求要拆成两类实时推送型比如服务器告警、订单状态变更。目标是一秒钟内让群里的人看到越及时越好。任务群发型比如运营活动、每日汇总、节日问候。目标是把消息发出去但不需要跟着业务事件实时触发可以走“任务审批成员确认”的模式。不同的类型对应的实现方式完全不同。实时推送我最后用的是群机器人 Webhook运营群发则用客户群群发 API。需求类型推荐方案是否即时送达是否需要人工确认适合场景实时通知群机器人 Webhook是否告警、订单提醒、自动化通知运营群发客户群群发 API否生成群发任务是群主/成员确认发送营销活动、常规触达一对一触达客户联系群发 API否是销售回访、个性化内容这套认知理顺之后再去看具体接口就不会绕路。2. 最直接方案群机器人 Webhook一条 HTTP 请求推消息进群如果你只是想把消息主动推进某个外部群最省事的接口其实是群机器人。2.1 怎么拿到群机器人的 Webhook 地址进入目标群的群设置找到“群机器人”添加一个自定义机器人。创建完成后企业微信会给你一个 Webhook 地址格式大致是https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key这里是一串密钥这个地址本身就是一个 API 入口。只要向它发 POST 请求消息就会以机器人身份出现在群里不需要企业微信后台配置复杂的权限也不需要获取 access_token。我用的第一个生产环境案例就是把这个 Webhook 接入了内部的订单系统。客户一下单服务端直接拼接消息体发过去群里的客户和售后同事都能第一时间看到进度。整个过程没有经过任何人的手动确认。2.2 发一条文本消息试试先看最基础的文本消息用 curl 就能验证curl https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key你的Key \ -H Content-Type: application/json \ -d { msgtype: text, text: { content: 您好您的订单已进入打包发货阶段。 } }正常返回是{ errcode: 0, errmsg: ok }只要 errcode 是 0消息就发出去了。如果是在 Python 服务里调用我习惯把它封装成一个通用函数import requests def send_qw_group_message(webhook_url: str, content: str) - bool: payload { msgtype: text, text: { content: content } } resp requests.post(webhook_url, jsonpayload, timeout10) result resp.json() if result.get(errcode) ! 0: print(f发送失败: {result}) return False return True注意几点请求头要设置Content-Type: application/json但实际传参时用jsonpayload即可requests 库会自动处理编码。连接超时不要设太长。我踩过坑某个 Webhook 地址因为网络原因一直卡住导致业务接口响应变慢最后统一加了 timeout 参数才解决。Webhook 地址一定要走服务端保存不能下发到前端页面里让别人拿到否则任何人都可以往你的群里发垃圾消息。2.3 常用的消息类型不只是纯文本机器人 Webhook 支持多种消息类型实际用得最多的是 text、markdown、image、news、file 这几种。markdown 类型的消息很适合做格式化通知payload { msgtype: markdown, markdown: { content: ## 订单异常提醒\n 订单号font color\warning\SO20240501001/font\n 状态font color\comment\发货失败/font\n 处理人仓库 A 组 } }news 类型则适合带跳转链接的消息payload { msgtype: news, news: { articles: [ { title: 6月活动预告, description: 本周五会员日全场折扣提前看, url: https://example.com/activity, picurl: https://example.com/promo.png } ] } }在实际项目中markdown 是使用频率最高的。告警类消息用不同颜色区分严重级别运营通知用标题加列表可读性比纯文本高一大截。但要注意markdown 消息的展示效果在不同客户端上存在细节差异不能用过于复杂的语法简单标题加引用加颜色就足够了。2.4 机器人的频率限制与常见坑机器人看着简单但它是有隐性的“脾气”的。最大的限制是频率。单个机器人短时间内发送频率过高会被限流甚至直接拒绝。企业微信目前的策略大致是每分钟限制多少条、每天限制多少条具体的数值会随产品策略调整所以我不建议把数值写死在代码里指望永远不变而是要做两件事在代码里捕获返回的错误码遇到频率限制时转入退避重试或告警降级。重要消息和备份通道做好切换。比如说如果机器人 Webhook 被限流了至少能退化为发邮件或者发到内部群。另外一个很容易忽略的问题机器人消息没有重发机制。你调接口返回成功了消息就到群里了如果返回失败你重发一次是新的消息不是对同一消息的补偿。对于“必须不重不漏”的业务场景需要自己维护消息幂等与业务状态不能依赖企业微信的推送结果。还有一旦有人把机器人从群里移除Webhook 立即失效。企业微信不会提前通知你。所以我在服务里做了个定时健康检查每天凌晨用一条测试消息探测所有关键群机器人是否还活着失效的自动告警出来。3. 带任务管理的方案客户群群发 API适合运营级群发Webhook 虽然直接但它有一个先天限制它只负责“投递”不关心“谁发的”。在更正式的运营场景里你需要知道这条消息是哪位成员发出去的、客户有没有拉黑、任务有没有超时这就轮到客户群群发 API 出场了。3.1 获取企业微信的 access_token群发接口走的是企业微信服务端 API需要先通过 corpid 和 secret 换取 access_tokenimport requests CORP_ID 你的企业ID SECRET 客户联系应用的Secret def get_access_token(corp_id: str, secret: str) - str: url https://qyapi.weixin.qq.com/cgi-bin/gettoken params { corpid: corp_id, corpsecret: secret } resp requests.get(url, paramsparams, timeout10).json() if resp.get(errcode) ! 0: raise RuntimeError(f获取access_token失败: {resp}) return resp[access_token]这里的 secret 是“客户联系”应用的 secret不是随便一个自建应用的 secret。如果你的企业微信没有开通客户联系功能群发相关的接口会提示无权限。这一点我在开始的时候踩了不少坑先确认权限再调代码。3.2 获取客户群列表要群发首先得拿到客户群的 ID。客户群列表接口可以按条件筛选群聊def get_customer_group_list(access_token: str, cursor: str ): url https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_customer_group_list params {access_token: access_token} body { limit: 100, cursor: cursor } resp requests.post(url, paramsparams, jsonbody, timeout10).json() return resp返回结果里会有一组 group_chat_list每个群包含 chat_id。拿到 chat_id 之后你才能创建群发任务。如果只是想向指定群发也可以跳过“列表”这一步直接把已知的 chat_id 填到群发接口里。但查列表能帮你确认这个群是否还存在、群名是否对得上避免拿到一个已经解散的群 ID。3.3 创建群发任务add_msg_template 接口核心接口是POST /cgi-bin/externalcontact/add_msg_template请求体大致是这样body { chat_type: group, chat_id_list: [wrOgQhDgAAcwMTBZ3P8mQ1IuHkYyJ01w], text: { content: 本周六会员日全场商品享 8 折点击小程序查看详情。 }, attachment: { msgtype: link, link: { title: 会员日专场, picurl: https://example.com/pic.png, desc: 精选好物限时折扣, url: https://example.com/sale } } }调用之后会返回一个 msgid这个 msgid 就是群发任务的标识{ errcode: 0, errmsg: ok, msgid: msgxxxxx }但这里有一个最关键、也是最容易被新手误解的点add_msg_template 不会把消息立刻送到群里。它只是创建一个群发任务。任务生成之后对应客户群的群主或管理员会在企业微信 App 里收到一条待发送提醒需要他们在客户端手动点击确认消息才会真正发出去。如果群主不点任务就一直挂着。在企业微信产品设计里这个机制是为了防止企业随意粗暴地触达客户把“群发”这个动作的最后一步交给真人而不是让后台脚本无节制地轰炸。所以如果你追求的是“服务器一调消息立刻到群里”的体验这个接口并不合适。3.4 群发之后的查询与跟进群发任务创建后可以用发送结果接口查询执行情况GET /cgi-bin/externalcontact/get_group_send_result?access_tokenACCESS_TOKENmsgidMSGID返回结果里能看到每个群的状态比如是否已发送、是否被客户拒收等。我建议做运营群发时不要只发不管而是在创建任务后隔一段时间轮询一次结果把“未发送”的群单独捞出来安排人工跟进。群发 API 还有一条很重要的限制同一个客户每天最多只能收到一条来自企业的群发消息。也就是说如果你今天已经给一批客户群里发过消息了再发第二条接口会直接报错。这个限制是为了保护用户体验做高频率运营的人必须做好排期别等到当天去撞限流。3.5 Webhook 和群发 API 怎么选这两者不是替代关系而是互补关系。对比维度群机器人 Webhook客户群群发 API即时性即时推送需要成员确认非即时接入成本极低一个URL即可需要开通客户联系、换 access_token消息类型text、markdown、image、news、file文本、图片、链接、小程序等是否计入防骚扰限制按机器人频率限制按客户每日群发上限限制运营数据无发完即结束可查询发送状态典型场景告警、实时通知营销活动、定期触达实时事务类消息我全部走 Webhook有运营节奏、需要留痕和追踪的批量消息走群发 API。结合起来用基本能覆盖外部群主动推送的全部主要场景。4. 调用细节与错误码排查为什么你的消息就是发不出去这类接口报错大多不是代码逻辑问题而是权限、token、参数格式这三类。4.1 access_token 相关的常见错误出现频率最高的是这几类错误码错误含义常见原因40001不合法的 secretcorpid 和 secret 不匹配或 secret 配错了应用42001access_token 已过期token 没有走缓存重复获取后过期40014不合法的 access_tokentoken 拼写错误或已经过期且未刷新48002接口无权限未开通客户联系功能或 secret 不是“客户联系”应用的60020访问来源 IP 不合法服务端出口 IP 不在企业微信后台的可信 IP 白名单里最后一个我印象很深。第一次在测试环境联调群发接口时明明本地跑得好好的部署到服务器上就开始报错。查了半天才发现企业微信后台对调用接口的服务器 IP 有白名单限制。你需要登录企业微信管理后台把服务器的公网出口 IP 加到“客户联系”或“应用管理”的对应信任列表里。4.2 接口返回成功群里却收不到消息这一类问题在 Webhook 和群发 API 里都可能出现。Webhook 机器人返回失败多半是频率限制。返回成功但没消息先检查机器人是不是已经被移出群了。我之前遇到过部署环境里误用了旧 Webhook 地址的情况数据是发到老的测试群去了调试群里当然看不到消息。群发 API 返回成功但群里没消息十有八九是卡在了“成员确认”这一步。任务已经生成但群主没有在客户端点发送或者群主属于不活跃成员一直没看到待办。另一种情况是接口返回了 group_chat_id 不存在或已失效。外部客户群如果已经解散或者企业成员主动退出了群聊群 ID 也会失效。所以调接口之前先查一遍客户群列表确认这个 chat_id 还在再发起群发。4.3 联调环境的搭建建议企业微信开发有一个很方便的机制每个企业可以有多个自建应用但生产环境的应用和测试环境的应用最好完全分开。我用的是“测试企业”模式申请一个测试企业微信账号在里面创建客户联系应用添加几个模拟客户所有接口联调都在测试环境完成。这样有几个好处不会误发消息给真实客户。可以反复调整消息内容和频率限制不用担心被投诉。错误码返回后再 debug不会污染生产数据。等测试环境全部验证完再替换成生产环境的 corpid 和 secret代码一行不用改直接上线。这个习惯帮我躲过了不少线上事故。5. 真实的落地姿势从告警机器人到定时群发任务最后分享一套我目前在生产环境实际使用的组合方案供参考。5.1 写一个通用的 Webhook 发送模块不管是告警还是定时通知我都会先写一个具备基础能力的发送模块import hashlib import time import requests class QwGroupNotifier: def __init__(self, webhook_url: str): self.webhook_url webhook_url def send_text(self, content: str, dedup_key: str ): if dedup_key: # 简单的幂等去重避免重复推送 digest hashlib.md5(dedup_key.encode()).hexdigest() if self._seen(digest): return True payload { msgtype: text, text: {content: content} } resp requests.post(self.webhook_url, jsonpayload, timeout10) return resp.json()幂等去重很重要。告警系统经常会出现同一问题在短时间内反复触发的情况如果不做去重群消息会被刷屏客户和同事都会崩溃。5.2 组合一套定时群发任务运营侧的每日通知我会用定时任务配合群发 API 来跑。这里的流程是每天固定时间扫描需要发送的客户群列表。调 add_msg_template 创建群发任务。等待一段时间后查询 get_group_send_result。把未发送的群单独汇总成表发给运营同事去跟进。这样做的好处是虽然群发需要人工确认但你至少把“创建任务”这一步自动化了运营只需要负责“点确认”省去重复编辑内容的时间。5.3 个人在实际操作中的几条体会第一外部群消息的内容一定要克制。群发 API 有官方的每日上限但就算没有限制也不意味着可以无节操地刷屏。外部群的成员本质上还是微信客户长期高频触达会带来退群和拉黑。我在实践中控制运营消息的频率通常一周不超过两条效果比天天发要好得多。第二Webhook 地址和 secret 一样属于敏感信息。别把它放在前端代码里也不要写进公开仓库。之前有人把测试环境的机器人地址提交到 GitHub 上导致机器人被脚本刷了一整天垃圾消息最后只能删掉机器人重新创建。第三错误处理要做得冗余。企业微信接口也会偶发超时不能因为一次失败就放弃整个任务。我的策略是文本通知类消息重试两次采用指数退避群发任务则记录失败状态等待下次轮询补发。第四不要把“发出去”等同于“客户看到了”。Webhook 返回 ok只能代表消息送到了企业微信服务器不代表每个群成员都打开了群。运营数据要想准确还是要依赖企业微信侧的数据报表不能只凭发送日志做判断。外部群主动发消息本质上是一个围绕“触达边界”的设计题。Webhook 负责快群发 API 负责稳把两者用对场景你就能在不骚扰客户的前提下把该推的消息推到位。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑