NEW

免费试用已开放

立即开始

API · SDK · 文档与调试

企业微信消息接口怎么用

更新于 2026-08-169 分钟

企业微信消息接口是所有接入里第一个被调用、也是最后一个被调对的接口。发一条文本很容易,难的是这三件事:五六种消息类型该按什么标准挑、同一个会话里两条消息的先后顺序凭什么保证、以及一次调用超时之后到底该不该重发。这三件都不是查字段能解决的,得先做判断再写代码。下面按 wecomapi 的消息接口形态逐个说。

消息类型按失败方式选,不是按内容选

选类型时大家的第一反应是「内容是什么就用什么类型」:有图用图片,有链接用卡片。这个标准在顺利的时候没毛病,出问题时你会发现真正该问的是另一件事 —— 这个类型失败时会怎么失败,以及失败之后能不能降级。企微消息接口这块,选型做在前面比重试写得好更省事。

按这个标准排下来,文本是最稳的一档:一步发出、没有中间态、失败了重发就是同一条。素材类(图片、文件、视频)一般要先把内容交给平台、拿到一个引用再发送,具体是不是两步以文档为准 —— 但只要是两步,你就多了一个中间态:引用拿到了而消息没发出去。这个中间态必须落库,否则重试的时候你不知道该从哪一步开始,重头再来又会多传一份。

卡片和富文本这类结构化消息是第三档,它们的问题不在发送而在渲染。不同客户端版本的展示不完全一致,字段长了会被截断,而截断是不报错的。判断是:凡是内容本身构成业务事实的东西(金额、单号、时间、截止日期),不要只放在卡片的次要位置上。一条朴素的纯文本兜底,比一张好看的卡片被截掉半句要重要得多。

  • 是一步还是两步?两步就要有中间态,而且中间态要能被重试正确识别
  • 内容会不会被截断?会的话关键信息前置,别指望接收方去展开
  • 失败了有没有降级路径?卡片降文本是最常用的一条,值得默认实现
  • 接收方看不到富样式时,这条消息还成立吗?不成立就说明你把事实放错了位置

实现上建议在自己这边先定义一层消息模型,再由发送层翻译成 wecomapi 的具体类型。业务代码只说「发一条订单发货通知」,至于翻译成卡片还是文本、降级怎么走、超长怎么截,全部收在一处。等到哪天要给一批老版本客户端统一降级,改一个文件就够,而不是去二十个业务分支里找。

发送成功不等于送达

调用返回 200 且业务码正常,准确的含义是「这次请求被受理了」。它和「对方看到了」之间还隔着一段投递。多数时候这两者差不了几百毫秒,于是很多系统就把受理当送达用了 —— 直到某天要回答「这条通知到底发出去没有」,才发现库里根本没有能回答这个问题的字段。

正确的形态是给每条外发消息建一个状态,而不是只记一条调用日志。最小的一组状态是四个:待发(业务决定要发、还没提交)、已提交(接口受理了)、已确认(有旁证说明它确实到了)、已放弃(重试用尽或主动丢弃)。第三个状态的旁证从哪来,取决于这个能力有没有对应的事件;没有的话就承认你只能停在「已提交」,并在产品文案上如实表达,而不是拿受理冒充送达。

「待发」这个状态最容易被省掉,省掉的代价在进程崩溃的那一刻兑现:先发请求再记录,进程恰好在两步之间被杀,这条消息发没发过就永远说不清了。顺序不能反 —— 先落待发,再提交,回来改状态。

示意:状态先落,受理只推到 submittedjavascript
// 示意逻辑:端点与字段仅作演示,精确定义以线上文档为准
// 顺序不能反:先落状态,再发请求
const rec = await outbox.create({
  bizRef: orderId,      // 你自己的业务关联 ID,事后对账全靠它
  to:     toId,
  state:  "pending",
});

try {
  const res = await post("https://manager.wecomapi.com/message/sendText", {
    guid, toId, content,
  });
  // 受理不等于送达:只推到 submitted,并把平台请求标识存下来
  await outbox.update(rec.id, {
    state:       "submitted",
    traceId:     pickTraceId(res),
    submittedAt: Date.now(),
  });
} catch (e) {
  // 没拿到响应时不要直接标 failed —— 它可能已经发出去了
  await outbox.update(rec.id, { state: classifyFailure(e) });
  throw e;
}

同一个会话的顺序,得你自己保证

并发发两条消息到同一个会话,到达顺序不保证等于你的调用顺序。网络、重试、对端排队,任何一环都能把顺序打乱。单条消息的场景无所谓,但只要你的业务里存在「两条消息组成一次表达」,这就是个会被客户直接看见的 bug:先收到「以上是您本月的账单明细」,几百毫秒后才收到明细本身。

解法只有一条:同一会话的在途消息数限制成 1,会话级串行、会话之间照常并行。这条规则的完整推导,以及入向那一半的乱序来源,站内讲消息时序那篇已经拆过。这里只补发送侧一个容易漏的点:队列分区只保证消费顺序,最终发送那一步如果是并发的,顺序照样会乱 —— 串行要卡在发送这一环。

更值得重新设计的是「两条消息组成一次表达」这件事本身。第一条成功、第二条失败,客户看到的是半句话,而且你没法撤回那半句。能合成一条就合成一条;确实要拆(比如正文加附件),就把关键信息放进第一条,让第二条即使丢了也不影响理解。

  • 会话级串行和账号级并发是两个约束,别共用同一个信号量 —— 它们的调整方向经常相反
  • 顺序敏感的消息带序号落库,重发时按序号补,不要按时间戳补 —— 重试之后时间戳已经乱了
  • 会话内串行只覆盖你自己发出去的消息。对方同时也在发,别假设你能控制整个会话的呈现顺序

失败重发:先问客户看到了什么

重发决策通常按错误码分类做,这在通用的接口调用上是对的。消息场景要多问一层,因为这里的重复不是多一次无谓的调用,是客户手机上多了一条一模一样的内容。所以分类维度换掉:不按错误码分,按「对方现在看到了什么」分。

  1. 1明确没发出去 —— 参数被拒、权限不覆盖、内容不被接受。客户看到 0 条。参数和权限类重发没有任何意义,回去改;内容类要走降级或转人工,别把同样的内容再撞一次,撞多了影响的是账号本身。
  2. 2不知道发没发出去 —— 超时、连接断、响应丢在回程。客户可能看到 0 条也可能看到 1 条。这是唯一真正需要幂等键的情形,而键必须在第一次提交之前就生成好并落库,事后补一个等于没有。键怎么拼、存多久,站内讲频控与重试那篇展开过。
  3. 3发出去了但内容不对 —— 截断、变量没渲染、发错了人。客户看到了错的。这一类不能靠重发解决,重发只会变成两条错的。处置是补一条更正,并且立刻停掉同一批次剩下的发送。

第三类是消息接口独有的,也最容易到事故复盘时才第一次被讨论。它几乎总是模板层的问题,不会只错一条 —— 所以批量发送任务里一定要有一个「立即停」的开关,而且这个开关要能在不重启服务的前提下按批次生效。等着改配置重新发版的那十分钟,够发出去几千条错的。

还有一类失败很容易漏:发出去了,但不该发。定时任务把昨天积压的提醒在今天早上七点补出去,技术上每一条都成功。所以重发之前要看的不只是错误类型,还有这条消息的保质期 —— 给每类消息定一个 TTL,超时就放弃并记一笔,而不是继续重发。这个判断要做在发送层,也就是收在调用 wecomapi 的那一处入口上;放在业务层的话,各个业务分支各写各的重试,总有一条绕过去。

什么时候该放弃,以及放弃之后做什么

重发上限不该是一个全局常量。它由两个业务参数决定:这条消息的保质期,以及它失败之后有没有替代路径。还能发短信、还能在自己 App 里推、还能让客服打个电话的,可以早点放弃;没有替代路径的关键通知得多试几次,但也不是无限试。企业微信消息API开发做到后期,真正吃时间的不是发送本身,就是这些兜底路径。

  • 定 TTL 而不是定次数。次数乘退避间隔的总时长才是你真正关心的量,直接按总时长写更清楚,也更好向业务解释
  • 放弃必须是一个显式状态,不是日志里的一行错误。没有状态就没有对账,没有对账就永远回答不了「今天有多少条没发出去」
  • 放弃后的动作按消息重要性分级:内部通知记一笔就够,客户侧的关键通知要触发告警或者切降级通道
  • 别把放弃的消息塞回原队列。它会在下一次积压时和新消息抢通道,把一次小故障拖成一次长故障

观测上只要两个数就能判断消息链路健不健康:提交后未确认的条数(它涨,说明确认路径断了或者投递在变慢),以及放弃率按原因拆开的分布(它的构成变了,说明模板、权限或者节奏出了问题)。这两个数比成功率有用得多 —— 成功率在受理口径下几乎永远好看,它是最不容易报警的那个指标。

本文讲的是选型判断与工程时序,不涉及具体字段。消息类型的支持范围、请求结构、错误码与端点路径以 wecomapi 线上接口文档为准,示意代码不要照抄上生产。

常见问题

发送接口返回成功,客户说没收到,怎么查?
先分清受理和送达。返回成功只说明请求被受理,要判断是否真的到达,得看这个能力有没有对应的事件作为旁证;没有旁证就承认系统只能停在「已提交」,别在产品上写成已送达。排查时用 wecomapi 响应里回传的那个请求标识加上发送时间去定位那一次调用,比按客户描述的时间段翻日志快得多。
同一个会话连发两条,顺序反了怎么办?
顺序不由调用顺序保证。把同一会话的在途消息数限制成 1,做到会话级串行、会话之间并行,并给顺序敏感的消息带上序号落库,重发按序号补而不是按时间戳补。更好的做法是先看能不能合成一条 —— 两条消息组成一次表达时,第二条失败客户就只看到半句话,而这半句撤不回来。
超时了到底该不该重发?
超时属于「不知道发没发出去」,直接重发有概率让客户连收两条。必须带一个业务侧生成、重试期间保持不变的幂等键,并且键要在第一次提交之前就落库。参数错误和权限不足重发没有意义;已经发错内容的更不能重发,只能补一条更正并停掉同批次剩余发送。具体的幂等支持方式以 wecomapi 文档为准。

准备好动手了?

精确字段、鉴权与端点以线上文档为准;可在控制台创建密钥后联调。

相关文章