星云API开发文档
星云API 官方开发文档 —— 覆盖登录、联系人、消息、群聊、朋友圈、文件等企业微信 API 接口的说明、请求参数、返回字段、代码示例与在线调试,帮助你快速接入。
多端云设备
支持 iPad 云端设备实例登录,iPad 设备支持三端同时在线。
三端在线登录代理方案
提供网络代理(按省份定位)、自定义 SOCKS5 代理、本地代理(Aid 辅助工具)三种登录方案,保证设备网络环境稳定。
网络稳定统一 API 规范
所有接口统一通过 POST 请求,以语义化路径区分操作。统一的请求/响应格式,降低对接成本。
统一规范快速开始
创建实例
注册账号并创建企业微信实例
获取 API Key
在控制台获取你的专属 API Key
调用接口
使用 API Key 调用第一个接口
配置 Webhook
接收事件推送与回调通知
正式上线
完成集成,开始使用星云API
curl -X POST 'https://api.xingyapi.com/api/message/sendText' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "instance_guid": "inst_xxxx", "conversationId": "会话ID(对方或群标识)", "content": "你好,欢迎了解星云。" }'
5 分钟快速开始
跟随以下步骤,快速接入星云API,发送第一条消息只需 5 分钟。
注册并登录控制台
在 open.xingyapi.com 注册账号,登录后进入开发者控制台。注册即自动开通体验版。
创建实例并扫码登录
在「实例与回调」点「一键登录」,用企业微信扫码上号,获得实例标识 instance_guid。
获取 API Key
在「API Key」页复制你的专属 Key(注册时已自动创建一个),请求时通过 X-Nebula-Key 头携带。
调用接口
用 API Key + instance_guid 调用业务接口,发送第一条消息。也可在控制台「在线测试」直接试。
接收回调(可选)
在「回调信息预览」实时查看实例收到的消息与事件;也可配置转发到你自己的服务器(见 Webhook 说明)。
基础信息准备
在开始之前,请确保你已经完成以下准备工作:
curl -X POST 'https://api.xingyapi.com/api/message/sendText' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "instance_guid": "inst_xxxx", "conversationId": "会话ID(对方或群标识)", "content": "你好,欢迎使用星云 API!" }'
请求规范与公共参数
在调用 API 前,建议先了解本页面中的统一请求规范。除特殊接口另有说明外,各接口均遵循这里定义的基础调用规则。
01 · 基础请求信息
| 项目 | 当前项目实际值 |
|---|---|
| Base URL | https://api.xingyapi.com |
| 请求协议 | HTTPS |
| 请求方式 | POST(当前全部接口均为 POST;具体以各接口页面标注为准) |
| Content-Type | application/json |
| 字符编码 | UTF-8 |
02 · 请求地址组成
接口完整地址 = Base URL + 接口路径(Path)。以「发送文本消息」为例:
03 · 请求 Header
| 参数 | 类型 | 说明 |
|---|---|---|
| X-Nebula-Key必填 | string | API 鉴权凭证,值为控制台「API Key」页的 Key(形如 nb_live_xxxx),无 Bearer 前缀。也可改用 Authorization: Bearer <API Key> 传递 |
| Content-Type必填 | string | 固定为 application/json |
04 · 公共参数
以下参数放在请求 Body(JSON)中,几乎所有业务接口都需要:
| 参数 | 类型 | 说明 |
|---|---|---|
| instance_guid必填 | string | 星云实例标识(inst_xxxx),标识本次操作的账号 / 设备实例。除「创建设备」等个别接口外,各业务接口均需传入 |
conversationId、content、roomId、userId、fileName、base64 等)为各接口的业务参数,仅在对应接口使用,请查看接口详情页。05 · POST 请求规范
- 请求体统一使用 JSON,请求头带
Content-Type: application/json。 - 业务参数放在 Body;鉴权凭证放在 Header(
X-Nebula-Key)。 - 字符串用双引号包裹;数字、布尔按 JSON 原生类型传递(如
true/false、数字不加引号)。 - 标记「必填」的字段不能缺省或为空。
- Body 必须是合法的 JSON 对象;若请求体不是 JSON 对象将返回
40001。
06 · 请求示例
curl -X POST 'https://api.xingyapi.com/api/message/sendText' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "instance_guid": "YOUR_INSTANCE_GUID", "conversationId": "会话ID(对方或群标识)", "content": "你好,欢迎使用星云 API!" }'
import requests
resp = requests.post(
"https://api.xingyapi.com/api/message/sendText",
headers={"X-Nebula-Key": "YOUR_API_KEY"},
json={
"instance_guid": "YOUR_INSTANCE_GUID",
"conversationId": "会话ID",
"content": "你好,欢迎使用星云 API!",
},
)
print(resp.json())
const resp = await fetch("https://api.xingyapi.com/api/message/sendText", { method: "POST", headers: { "X-Nebula-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ instance_guid: "YOUR_INSTANCE_GUID", conversationId: "会话ID", content: "你好,欢迎使用星云 API!", }), }); console.log(await resp.json());
07 · 统一返回格式
所有接口返回统一信封结构,业务数据在 data 中:
| 字段 | 类型 | 说明 |
|---|---|---|
| ok | boolean | 是否成功,true 为成功 |
| code | number | 业务状态码,0 表示成功,非 0 表示失败 |
| message | string | 结果说明;失败时为错误原因 |
| request_id | string | 请求追踪 ID(nb_ 前缀),排查问题时可提供给客服 |
| data | object | 业务数据,结构以各接口响应说明为准 |
08 · 成功响应示例
{
"ok": true,
"code": 0,
"message": "success",
"request_id": "nb_xxxxxxxx",
"data": {}
}
09 · 失败响应示例
{
"ok": false,
"code": 401,
"message": "API Key 无效或缺失",
"request_id": "nb_xxxxxxxx",
"data": null
}
10 · 错误码
如果请求失败,可根据响应中的 code 和 message 排查问题,完整说明请查看「错误码」。
11 · 注意事项
- 参数类型需与文档保持一致(字符串、数字、布尔不要混用)。
- 必填参数不能为空。
- API Key 请妥善保管,不要提交到公开代码仓库。
- 除个别接口(如创建设备)外,业务接口都需要传
instance_guid。 - 不同接口可能存在独立参数要求,具体以对应接口页面说明为准。
登录流程(新版)
通过 API 让企业微信账号扫码登录到你的实例:创建设备 → 获取二维码 → 轮询扫码状态 → 确认会话就绪,之后即可调用业务接口。登录后的掉线恢复、维护后重连都由平台自动完成。
POST,请求头带 X-Nebula-Key,返回统一信封 {ok, code, message, request_id, data}。请以响应体里的 code 判断结果(0 为成功),不要只看 HTTP 状态码。流程总览
创建设备
device/create 得到 instance_guid(只需一次)
获取个人信息(检查是否已登录)
personal/getInfo:新设备返回 no account 属正常,继续;uin 非 0 说明已在线,跳到第 8 步
获取二维码
login/getQr:展示二维码,记下 uuid
检测二维码
login/checkQr 每 2–3 秒一次:status=10 去第 5 步;status=2 跳到第 7 步
提交验证码(status=10 时)
login/submitQrCode 提交手机上显示的验证码
检测二维码(提交验证码后)
login/checkQr 继续查,直到 status=2 才算登录成功
获取个人信息(确认登录成功)
personal/getInfo:code=0 且 uin 非 0
更新回调地址(直连推送时)
long/updateCallbackURL 设置消息推送地址;用控制台回调转发则跳过。体验版不可用
实心圆为必经步骤,空心圆为按需步骤(不需要验证码时跳过第 5、6 步;用控制台回调转发时跳过第 8 步)。完成后即可开始业务调用。
1 POST /api/device/create → data.instance_guid(只需一次) 2 POST /api/personal/getInfo → 新设备:no account(正常,继续);uin≠0:已在线,跳到 8 3 POST /api/login/getQr → imageBase64 + uuid(有效约 10 分钟) 4 POST /api/login/checkQr → 每 2–3 秒一次,看 data.status ├ 0 / 1 继续查 ├ 10 需要验证码 → 第 5 步 ├ 2 登录成功 → 第 7 步 ├ 4 用户取消,停止 └ code=50001 且含 -12007 → 二维码过期,回到第 3 步 5 POST /api/login/submitQrCode → 提交验证码(仅 status=10) 6 POST /api/login/checkQr → 继续查,直到 status=2(必须) 7 POST /api/personal/getInfo → code=0 且 uin≠0 = 登录成功 8 POST /api/long/updateCallbackURL → 直连推送时设置推送地址(控制台转发则跳过;体验版不可用) 开始业务调用(如 /api/message/sendText) 退出:POST /api/login/logout → POST /api/long/stop
不需要验证码时(第 4 步直接返回 status=2),跳过「提交验证码」和第二次「检测二维码」。
常见错误:提交验证码后直接调「获取个人信息」,会返回
no account is currently logged in on this device——此时登录还没完成,先「检测二维码」查到 status=2 再调。体验版:不能调「更新回调地址」,请在第 4、6 步「检测二维码」时传
callbackUrl,或在控制台「实例」页配置回调。分步说明
每一步列出调用的接口、关键参数、成功判据和失败时的处理方式。点击接口名可查看完整参数。
- 接口:device/create
- 关键参数:
type固定传"iPad";name设备备注名(≤120 字,方便在控制台识别);version传0;proxy设备使用的代理(格式见下方「代理怎么传」),不用代理传空字符串"";建议与后续取码、扫码传同一个代理。 - 成功判据:
code=0,保存data.instance_guid(形如inst_xxxxxxxx),之后所有接口都用它。 - 失败怎么办:
40302账号额度已满(体验版 / POC 有账号数量上限)→ 申请企业 POC 或移除不用的实例;40301账号权益已到期 → 在控制台申请续期;50001按message检查参数;50000稍后重试;50002设备已创建但未拿到instance_guid→ 联系客服。 - 注意:设备创建成功只代表设备存在,不代表账号已登录。同一个
instance_guid退出后可以重新登录,不必每次新建(每个实例都会占用账号额度)。
- 接口:personal/getInfo,只需传
instance_guid。 - 成功判据:
code=0且data.uin非 0 → 该实例已有账号在线,无需再取码,直接跳到第 08 步。 - 未登录时:返回
code=50001,message为no account is currently logged in on this device→ 继续第 03 步。 - 适用场景:你的服务重启后、用户重复点击「登录」时,先查一次可避免重复登录。
- 接口:login/getQr
- 关键参数:
instance_guid;proxy登录代理,不用代理传空字符串""(格式见下方「代理怎么传」)。 - 成功判据:
code=0,返回imageBase64(二维码 PNG 图片的 Base64,约 50–60KB,展示时加前缀data:image/png;base64,)、uuid(本次二维码编号,32 位,第 04、05 步都要用)、refreshInterval(二维码有效期,单位秒,通常为600,约 10 分钟)。 - 说明:平台在取码前会自动完成长链准备,未就绪时在本次请求内自动重试(最长约 10 秒),无需额外调用其他接口。因此本接口可能要几秒才返回,建议请求超时设为 30 秒左右。
- 失败怎么办:
50001按message处理;50000稍后重试。每次取码都会得到新的二维码,请始终使用最近一次返回的uuid。
- 接口:login/checkQr,每 2–3 秒调用一次,直到终态。
- 关键参数:
uuid(必填,第 03 步返回);proxy(与第 03 步相同);pushHistory(是否推送历史消息,默认false,设为true时登录后可能推送大量历史消息);callbackUrl(可选,直连推送地址,见下方「回调(消息推送)怎么配」)。 - 成功判据:
data.status = 2。此时data还会附带扫码账号资料(如nickName、avatarUrl、corpId、corpUserId、corpBriefInfo企业简要信息等,以实际返回为准),其中加密 / 内部字段无需处理。 - 注意:遇到终态(status=2 / 4,或
code=50001失败)后立即停止轮询,成功后不要继续轮询;只有code=50000(服务暂时不可用)可以继续下一轮。新版中如果没传callbackUrl,登录后该实例可能收不到任何消息推送:使用控制台转发的,请在登录成功后到控制台「实例」页再保存一次回调地址,详见下方「回调(消息推送)怎么配」。
| 返回 | 类型 | 含义 | 你该怎么做 |
|---|---|---|---|
| status = 0 | 继续轮询 | 尚未扫码 | 2–3 秒后再查 |
| status = 1 | 继续轮询 | 已扫码,等待手机上确认 | 提示用户在手机企业微信上确认登录,继续轮询 |
| status = 10 | 需要处理 | 需要输入登录验证码 | 让用户输入手机企业微信上显示的验证码,按第 05 步提交,然后做第 06 步 |
| status = 2 | 终态 · 成功 | 扫码认证成功 | 立即停止轮询,进入第 07 步确认登录成功 |
| status = 4 | 终态 | 用户在手机上取消了登录 | 停止轮询;用户想重试时从第 03 步重新取码 |
| code = 50001 message 含 -12007 | 终态 · 需重新取码 | 二维码已过期,或 uuid 无效(如 -12007|get expired data empty) | 不要再用这个 uuid 轮询,回到第 03 步重新取码 |
| code = 50001 其他 message | 终态 | 其他失败,原因见 message | 按 message 排查,必要时从第 03 步重新取码 |
| code = 50000 | 可重试 | 服务暂时不可用 | 稍后继续下一轮轮询即可 |
- 接口:login/submitQrCode,仅当第 04 步返回 status=10 时调用。
- 关键参数:
uuid(本次二维码);code(手机企业微信上显示的登录验证码);proxy(必须与第 03、04 步相同,否则相当于同一次登录换了网络出口,可能校验失败或触发安全验证)。 - 成功判据:
code=0(data为空对象)。提交成功不代表登录完成,接着做第 06 步。 - 失败怎么办:按
message提示处理(如验证码输错,可让用户重新输入后再提交);若轮询返回-12007,回到第 03 步重新取码。
- 接口:login/checkQr,参数与第 04 步完全相同(同一个
uuid、proxy)。 - 做法:每 2–3 秒调用一次,直到返回 status=2 才算登录成功,然后进入第 07 步。这一步不能省:提交验证码后直接调「获取个人信息」会返回
no account is currently logged in on this device。 - 失败怎么办:返回
-12007(二维码已过期)→ 回到第 03 步重新取码;同一个instance_guid可复用,不用重新创建设备。
- 接口:personal/getInfo
- 做法:检测二维码返回 status=2 后调用;如果还没就绪,每 0.5–1 秒再调一次,最多约 10 秒,直到
code=0且data.uin非 0。status=2 只代表扫码认证成功,不保证会话已立即可用。 - 成功后:可保存
uin(账号 ID)、name、corpId等资料。超过 2^53-1(9007199254740991)的大整数会以字符串返回,判断uin时建议统一转成字符串再与"0"比较。 - 约 10 秒仍未就绪:稍后再查一次;如持续返回
no account is currently logged in on this device,说明登录未成功,请从第 03 步重新取码。
- 接口:long/updateCallbackURL,参数
instance_guid、callbackUrl(你自己的公网 HTTP/HTTPS 接收地址)。 - 作用:登录后消息直接推送到这个地址(不经平台签名转发)。成功返回
{instance_guid, callbackUrl}。 - 什么时候跳过:已在控制台「实例」页配置回调(平台签名转发)时不要调用,否则会覆盖平台转发;第 04/06 步已传
callbackUrl时也可跳过。 - 体验版:不能调用本接口,请改为在第 04/06 步「检测二维码」时传
callbackUrl,或在控制台「实例」页配置回调。
- 例如用 message/sendText 发送文本消息;所有业务接口的请求体都带同一个
instance_guid。 - 登录后的掉线恢复、维护后重连由平台自动完成,见下方「登录后平台自动做什么」。
关键返回示例
切换标签查看各步骤的典型返回(成功响应中的账号资料为节选,完整字段见对应接口详情)。
{
"ok": true,
"code": 0,
"message": "success",
"request_id": "nb_xxxxxxxx",
"data": {
"imageBase64": "iVBORw0KGgoAAAANSUhEUgAA…(PNG 图片 Base64,约 50–60KB,此处省略)",
"uuid": "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6",
"refreshInterval": 600
}
}
{
"ok": true,
"code": 0,
"message": "success",
"request_id": "nb_xxxxxxxx",
"data": {
"status": 0,
"uuid": "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6"
}
}
{
"ok": true,
"code": 0,
"message": "success",
"request_id": "nb_xxxxxxxx",
"data": {
"status": 2,
"uuid": "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6",
"nickName": "示例用户",
"avatarUrl": "https://example.com/avatar.png",
"corpId": 1970320000000001,
"corpUserId": 1688850000000001,
"corpLogoUrl": "https://example.com/logo.png",
"confirmedFlag": true
}
}
{
"ok": false,
"code": 50001,
"message": "-12007|get expired data empty",
"request_id": "nb_xxxxxxxx",
"data": null
}
{
"ok": false,
"code": 50001,
"message": "no account is currently logged in on this device",
"request_id": "nb_xxxxxxxx",
"data": null
}
{
"ok": true,
"code": 0,
"message": "success",
"request_id": "nb_xxxxxxxx",
"data": {
"uin": 1688850000000001,
"name": "示例用户",
"corpId": 1970320000000001,
"gender": 1,
"iconUrl": "https://example.com/avatar.png",
"position": ""
}
}
完整调用示例(cURL)
按步骤编号依次执行;把 YOUR_API_KEY 换成控制台「API Key」页的 Key,把 inst_xxxxxxxx 和 uuid 换成实际返回值。
# 01 创建设备(只需一次):保存返回的 data.instance_guid curl -X POST 'https://api.xingyapi.com/api/device/create' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"type":"iPad","name":"客服设备01","version":0,"proxy":""}' # → {"ok":true,"code":0,"message":"success","request_id":"nb_xxxxxxxx","data":{"instance_guid":"inst_xxxxxxxx"}} # 02 获取个人信息(检查是否已登录):新设备返回 no account(正常);uin 非 0 → 已在线,跳到 08 curl -X POST 'https://api.xingyapi.com/api/personal/getInfo' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx"}' # 03 获取二维码:展示 data.imageBase64,记下 data.uuid(不用代理 proxy 传 "") curl -X POST 'https://api.xingyapi.com/api/login/getQr' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx","proxy":""}' # 04 检测二维码:每 2–3 秒一次;status=10 → 05;status=2 → 07;-12007 → 回到 03 重新取码 # 直连推送:每次都带同一个 callbackUrl;使用控制台转发:去掉 callbackUrl 字段 curl -X POST 'https://api.xingyapi.com/api/login/checkQr' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx","uuid":"A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6","proxy":"","pushHistory":false,"callbackUrl":"https://example.com/wecom/callback"}' # 05(仅 status=10 时)提交手机企业微信上显示的验证码 curl -X POST 'https://api.xingyapi.com/api/login/submitQrCode' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx","uuid":"A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6","code":"123456","proxy":""}' # 06(提交验证码后)再检测二维码:每 2–3 秒一次,直到 data.status=2(必须,否则登录未完成) curl -X POST 'https://api.xingyapi.com/api/login/checkQr' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx","uuid":"A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6","proxy":"","pushHistory":false}' # 07 获取个人信息(确认登录成功):code=0 且 data.uin 非 0(未就绪时每 0.5–1 秒再查,最多约 10 秒) curl -X POST 'https://api.xingyapi.com/api/personal/getInfo' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx"}' # 08(直连推送时)更新回调地址:消息直接推到你的地址;用控制台回调转发则跳过;体验版不可用 curl -X POST 'https://api.xingyapi.com/api/long/updateCallbackURL' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx","callbackUrl":"https://example.com/wecom/callback"}' # 开始业务调用,例如发送文本消息(把 YOUR_CONVERSATION_ID 换成对方或群的会话 ID,可从收到的回调消息里获取) curl -X POST 'https://api.xingyapi.com/api/message/sendText' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx","conversationId":"YOUR_CONVERSATION_ID","content":"你好"}' # 退出:先退出账号,再关闭长链(设备上已无账号时返回 no account …,可视为已退出) curl -X POST 'https://api.xingyapi.com/api/login/logout' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx"}' curl -X POST 'https://api.xingyapi.com/api/long/stop' \ -H 'X-Nebula-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"instance_guid":"inst_xxxxxxxx"}'
轮询示例(Python)
完整演示:已登录直接返回 → 取码 → 每 2.5 秒轮询 → 终态停止 → -12007 自动重新取码 → status=10 提交验证码 → 成功后用 getInfo 确认会话就绪。
import time import requests BASE = "https://api.xingyapi.com" HEADERS = {"X-Nebula-Key": "YOUR_API_KEY"} INSTANCE = "inst_xxxxxxxx" # 第 01 步 device/create 返回的 instance_guid PROXY = "" # 不用代理传 "";用代理时第 03 / 04 / 05 步必须相同 CALLBACK = "" # 直连推送地址;使用控制台转发时保持为空 def call(path, body): # 以响应体里的 code 判断结果(失败时 HTTP 状态码可能不是 200) return requests.post(BASE + path, json=body, headers=HEADERS, timeout=30).json() def is_online(res): # code=0 且 uin 非 0 才算真正在线;uin 可能是数字也可能是字符串 data = res.get("data") or {} return res.get("code") == 0 and str(data.get("uin") or 0) != "0" def show_qr(image_base64): # 换成你自己的展示逻辑,例如网页里 <img src="data:image/png;base64,..."> print("请用企业微信扫码,二维码图片长度:", len(image_base64)) def get_qr(): res = call("/api/login/getQr", {"instance_guid": INSTANCE, "proxy": PROXY}) if res.get("code") != 0: raise RuntimeError("取码失败:" + str(res.get("message"))) show_qr(res["data"]["imageBase64"]) return res["data"]["uuid"] def wait_ready(timeout=10): # 第 07 步:获取个人信息,确认登录成功 deadline = time.time() + timeout while time.time() < deadline: res = call("/api/personal/getInfo", {"instance_guid": INSTANCE}) if is_online(res): return res["data"] time.sleep(1) return None def login(max_refresh=3): # 第 02 步:已登录就直接返回,避免重复取码 res = call("/api/personal/getInfo", {"instance_guid": INSTANCE}) if is_online(res): return res["data"] uuid = get_qr() # 第 03 步 refreshed = 0 busy = 0 # 连续 50000 的次数 while True: # 第 04 步:每 2.5 秒轮询一次 time.sleep(2.5) body = {"instance_guid": INSTANCE, "uuid": uuid, "proxy": PROXY, "pushHistory": False} if CALLBACK: body["callbackUrl"] = CALLBACK res = call("/api/login/checkQr", body) if res.get("code") != 0: msg = str(res.get("message") or "") if "-12007" in msg and refreshed < max_refresh: refreshed += 1 # 二维码过期 / uuid 无效 → 重新取码 uuid = get_qr() continue if res.get("code") == 50000 and busy < 20: busy += 1 continue # 服务暂时不可用:下一轮再查(连续多次失败则放弃) raise RuntimeError("登录失败:" + msg) busy = 0 status = res["data"]["status"] if status in (0, 1): continue # 未扫码 / 等待手机确认 if status == 10: # 第 05 步:提交验证码 code = input("请输入手机企业微信上显示的验证码:") r = call("/api/login/submitQrCode", {"instance_guid": INSTANCE, "uuid": uuid, "code": code, "proxy": PROXY}) if r.get("code") != 0: print("验证码提交失败:", r.get("message")) continue # 第 06 步:提交后回到这里继续检测,直到 status=2 if status == 2: break # 认证成功:立即停止轮询 if status == 4: raise RuntimeError("用户在手机上取消了登录") raise RuntimeError("登录未完成,status=%s" % status) # 其他状态按失败处理 info = wait_ready() # 第 07 步 if not info: raise RuntimeError("会话暂未就绪,请稍后再调用 getInfo 确认") return info if __name__ == "__main__": me = login() print("登录成功,账号 uin:", me["uin"])
回调(消息推送)怎么配
新版重点变化:扫码轮询(checkQr)时如果不传 callbackUrl,登录后该实例可能不会推送任何消息——即使控制台已为该实例配置了回调地址,通过 API 扫码登录后也需要重新保存一次才会生效。请从下面两种方式中选一种:
控制台转发(平台签名)
在控制台「实例」页为该实例填写你的回调地址(HTTPS、公网可访问)。平台收到事件后签名转发(带 X-Nebula-Signature,格式见「Webhook 说明」「回调结构说明」),收到的事件也可在「回调中心」实时查看。此方式下 checkQr 不传 callbackUrl;通过 API 扫码登录成功后,请在「实例」页再保存一次回调地址,平台才会为本次登录登记推送。之后掉线自动恢复时,平台会自动重新登记。
直连推送
扫码轮询时每次都带同一个 callbackUrl(你自己的公网 HTTP / HTTPS 接收地址),登录后消息直接推送到该地址,不经平台签名转发(没有 X-Nebula-Signature,也不是平台包装后的 {instance_guid, events[]} 格式),控制台「回调中心」也看不到这些事件,请自行做好来源校验。
pushHistory 一般传 false;设为 true 时登录后会推送历史消息,量可能很大。代理怎么传
| 项目 | 说明 |
|---|---|
| 格式 | 有认证:socks5://用户名:密码@IP:端口;无认证:socks5://IP:端口;不用代理传空字符串 ""。示例:"proxy": "socks5://user:pass@203.0.113.10:1080" |
| 同一次登录保持一致 | getQr / checkQr / submitQrCode 必须传同一个 proxy;创建设备(device/create)时也建议传这个代理。中途更换相当于换了网络出口,可能校验失败或触发安全验证。 |
| 谁来决定 | 控制台一键登录会按所选省份自动分配代理;通过 API 登录时由你自行决定是否传入。 |
| 登录之后 | 代理只用于登录环节:登录成功约 30 分钟后,平台会自动把该实例切换为直连,无需你处理。 |
登录后平台自动做什么
维护后自动重连
平台维护或重启后,会自动为原本在线的账号重新建立连接,无需你重新登录或调用接口。
自动代理自动切直连
代理只用于登录环节:用代理登录的账号,登录成功约 30 分钟后平台自动切换为直连,无需你处理。
自动code=0 且 data.uin 非 0 即在线。不要用 long/check 判断——它只表示长链连接正常,不代表账号一定在线(且体验版不可用)。以下情况平台无法自动恢复,需要重新扫码(从第 03 步开始,instance_guid 可复用):账号在手机上退出登录或移除了该设备、在其他设备登录被挤下线、登录状态过期、触发安全验证等。此时 getInfo 会返回失败,message 为具体原因。
退出登录
/api/login/logout:退出当前登录的企业微信账号/api/long/stop:停止该实例的消息推送instance_guid 可复用- login/logout 退出后,该账号需要重新扫码才能再登录。
- long/stop 只关闭长链(停止接收推送),不等于退出账号;单独调用后,平台的自动保活可能在检测到掉线时重新开启长链。要主动停用,请按 logout → long/stop 的顺序调用,或在控制台移除该账号。
- 设备上已经没有登录账号时,这两个接口会返回
no account is currently logged in on this device,可视为已退出。
控制台一键登录
不想自己对接登录流程?在控制台「实例」页点「一键登录」,选择省份后用企业微信扫码即可——设备创建、登录代理和消息接收都由平台自动完成,登录成功后在实例列表里拿到 instance_guid,直接调用业务接口。
常见问题
登录相关的常见返回码速查(完整列表见「错误码」):
| code | 类型 | 含义 | 怎么处理 |
|---|---|---|---|
| 0 | 成功 | 请求成功,业务数据在 data | — |
| 50001 | 业务错误 | 调用失败,message 为失败原因原话(如 -12007|get expired data empty、no account is currently logged in on this device) | 按 message 处理,见下方问答 |
| 50000 | 服务器错误 | 服务暂时不可用 | 稍后重试;持续失败请联系客服 |
| 40301 | 接口限制 | 无权限:体验版仅开放 20 个基础接口,或账号权益已到期 | 在控制台申请企业 POC / 续期 |
| 40302 | 接口限制 | 创建设备时账号额度已满 | 申请企业 POC 扩容,或移除不用的实例 |
| 401 | 认证相关 | API Key 无效或缺失 | 检查请求头 X-Nebula-Key |
二维码有效期见 getQr 返回的 refreshInterval(通常 600 秒,约 10 分钟)。过期后 checkQr 会直接失败:code=50001,message 含 -12007(如 -12007|get expired data empty)。此时停止用旧 uuid 轮询,重新调用 getQr(同一个 instance_guid、同一个 proxy),展示新二维码并用新 uuid 继续轮询。传了错误或旧的 uuid 也会返回同样的错误。
status=0 表示还没人扫码:请确认用户用的是企业微信 App 扫码、展示的是最近一次取到的二维码、轮询用的是对应的 uuid。status=1 表示已扫码、等待手机确认:提示用户在手机上确认登录。超过有效期仍未完成会返回 -12007,按上一条重新取码。
需要输入登录验证码。让用户把手机企业微信上显示的验证码告诉你,调用 login/submitQrCode(uuid 与 proxy 必须与本次取码相同),然后继续轮询 checkQr 直到 status=2。
说明登录还没完成:提交验证码(login/submitQrCode)只是把验证码交上去,必须回到 login/checkQr 继续查,直到返回 status=2,账号才真正登录。之后再调 personal/getInfo 就会返回 code=0 和账号资料。如果 checkQr 返回 -12007(二维码已过期),从 login/getQr 重新取码扫码即可,同一个 instance_guid 可复用、无需重新创建设备。
用户在手机上取消了登录。停止轮询即可;用户想再次登录时从第 03 步重新取码。
- 先用
getInfo确认账号在线(code=0且uin非 0)。 - 检查推送是否配置:新版中
checkQr没传callbackUrl时,可能不推送任何消息(控制台已配置的,也要在 API 登录成功后重新保存一次)。按「回调(消息推送)怎么配」二选一配置。 - 控制台转发:先在「回调中心」看平台是否收到了事件——收到了但你的服务器没收到,检查地址是否为公网可访问的 HTTPS 地址、是否在 10 秒内返回
{"code":0};「回调中心」也没有事件、且是通过 API 扫码登录的,到「实例」页重新保存一次回调地址。 - 直连推送:确认地址公网可访问;两种方式会互相覆盖,可调用
/api/long/updateCallbackURL重新设置一次(体验版不可用,可重新扫码登录并在checkQr里带上callbackUrl)。
意思是「该实例上当前没有已登录的账号」(未登录、已退出或登录已失效)。getInfo 返回它 → 需要走登录流程(从第 03 步开始,instance_guid 可复用);logout / long/stop 返回它 → 可视为已经退出;业务接口返回它 → 账号已离线,需要重新扫码登录。
体验版仅开放 20 个基础接口,/api/long/check、/api/long/updateCallbackURL 不在其中。判断是否在线请用 /api/personal/getInfo(体验版可用);设置推送地址可用控制台或 checkQr 的 callbackUrl。需要全部接口请在控制台申请企业 POC。
不需要。新版中掉线恢复、维护后重连都由平台自动完成。如遇提示「长链未激活」,可调用 device/restoreLong(恢复长链)后重试。如果账号确实已失效(如在手机上退出、被挤下线),请重新扫码登录。
imageBase64 是纯 Base64 内容,不带前缀。网页展示时写成 <img src="data:image/png;base64,{imageBase64}">;也可以先解码保存为 PNG 文件再展示。
不算。long/stop 只关闭长链,账号仍在登录状态,平台的自动保活可能重新开启长链。要退出账号请先调用 /api/login/logout,再调用 /api/long/stop。
相关文档
更新日志
记录星云API平台的每一次功能更新与优化,帮助您及时了解最新动态。
补充请求参数 startIndex(首页传 0,之后传上一页返回的 nextStart);返回 nextStart=-1 表示已全部取完,不要把 -1 再传入(会从头重新翻页)。补全群列表各字段含义与翻页示例。接口本身无变化,原有调用不受影响。
/api/room/getMyCustomerGroupList实测该接口只返回最近约 6000 条消息范围内有往来的会话(范围起点 minSequence),不保证包含全部历史会话;需要完整清单请组合「获取我的客户群列表」「同步通讯录 id」等接口。同时补充 message.content / extraData、免打扰会话折叠状态 foldState 等字段说明。接口本身无变化。
/api/room/sessionSync返回字段名更正为实际返回的 conversationType / lastReadMessageId / minSequence,并补全未读数、最后一条消息、免打扰列表(shieldList)、已保存到通讯录的群(collectionList)等字段含义;说明当前版本一次返回全部会话,limit 不限制条数,以 isEnd 判断是否完整。接口本身无变化。
/api/room/sessionSync查询当前账号通过群发助手发出的群发记录:发送时间、发给客户还是客户群、发送进度(已发 / 总数)和发送内容类型。只需传 instance_guid,一次返回全部记录。控制台「在线调试 › 消息」可直接试调。
/api/message/getGroupSendRecord「下载大文件」已停用,请改用「获取大文件下载 url」;「开启长链」请改用「恢复长链」+「更新回调地址」(旧调用仍兼容);「断线重连」不再需要,掉线恢复由平台自动完成(旧调用仍兼容)。
/api/cdn/downloadBigFile/api/long/start/api/login/reconnect创建设备 → 获取个人信息 → 获取二维码 → 检测二维码 → 提交验证码 → 检测二维码 → 获取个人信息 → 更新回调地址。不需要验证码时跳过第 5、6 步;使用控制台回调转发时跳过第 8 步;体验版不能调用「更新回调地址」,可在「检测二维码」时传 callbackUrl 或在控制台配置回调。指南页、示例代码与各接口「调用时机」已按此编号更新。
/api/login/*/api/personal/getInfo/api/long/updateCallbackURL按实测跑通的流程补充说明:需要验证码时,顺序为 获取二维码 → 检测二维码(status=10)→ 提交验证码 → 检测二维码直到 status=2 → 获取个人信息。提交验证码后直接获取个人信息会返回 no account is currently logged in on this device,并已加入常见问题。
/api/login/submitQrCode/api/login/checkQr新增「恢复长链(开启长链)」:开启或恢复实例的长链,长链已开启时调用也返回成功。一般无需手动调用(平台在取码、掉线恢复、维护后重连时自动处理),遇到「长链未激活」或主动关闭长链后可手动调用。旧接口 /api/long/start 保留兼容,等同于「恢复长链」+「更新回调地址」。体验版可用。
/api/device/restoreLong/api/long/start「创建设备」参数调整为 type / name / version / proxy:创建时即指定设备使用的代理(不用代理传空字符串),lanIp 不再使用、传了会被忽略。未传 proxy 的旧调用按不使用代理处理,仍可正常创建。
/api/device/create新增指南页「登录流程(新版)」:创建设备 → 取二维码 → 轮询扫码状态(含状态码对照、二维码过期处理)→ 需要时提交验证码 → 确认会话就绪。重要变化:扫码轮询时不配置回调地址,登录后可能收不到消息推送,请在控制台为实例配置回调或在 callbackUrl 传入接收地址;同一次登录的取码 / 扫码查询 / 提交验证码须传同一个 proxy;登录后的掉线恢复由平台自动完成,/api/login/reconnect 不再建议使用;判断账号是否在线请用 /api/personal/getInfo(uin 非 0)。「更新回调地址」的返回改为 {instance_guid, callbackUrl}。
/api/device/create/api/login/*/api/long/*/api/personal/getInfo本次新增:个人资料(获取 / 更新个人信息、获取个人二维码)、标签管理(同步标签、操作标签 / 标签组)、群发助手(获取素材库列表、群发、发送待发送消息)、联系人(添加名片、添加群成员为好友、同意新客户、更新外部联系人信息、更新用户标签、设置同事备注、同步外部数据)、群管理(解散群、退出群聊、群置顶、禁止改群名、禁止群内互加、设置邀请确认、设置群备注)。完整清单与入参 / 出参见「接口详情」。
/api/personal/*/api/label/*/api/message/groupSend/api/contact/addCard/api/room/disband全部接口已更新到最新版本,多处入参 / 出参字段有变化,请以「接口详情」为准重新对接。主要迁移:个人信息类迁移至 /api/personal/*(如「获取个人信息」→ /api/personal/getInfo);部分群管理接口迁移至 /api/room/*。大整数 ID(会话 / 群 / 序号 / 成员等)在示例中统一以「字符串」形式给出,避免 JavaScript 解析时丢失精度,平台会自动转回数字。
/api/personal/getInfo群管理 → /api/room/*提供企业微信实例扫码托管、消息收发、通讯录、群聊、朋友圈、文件等语义化 API;支持在线测试、回调事件实时预览,以及体验版 / 企业 POC 权益体系。
https://api.xingyapi.com鉴权:X-Nebula-KeyWebhook 说明
实例登录后,它收到的企业微信消息与事件会被平台实时捕获。你有两种用法:① 在控制台「回调信息预览」直接查看,无需自建服务器;② 配置转发地址,平台把事件带签名 POST 到你的服务器。
工作原理
已登录实例收到消息 / 状态变更,上游推送到平台接收器
平台脱敏、解析后存入「回调信息预览」,并按需转发
控制台查看,或你的服务器收到 POST 后验签处理
方式一:控制台预览(零代码)
进入控制台「回调信息预览」,选择实例即可实时看到它收到的消息与事件(每 5 秒自动刷新),无需自建服务器。
方式二:转发到你的服务器
在「实例与回调」为实例配置回调地址,平台会把事件 POST 过去。
{"code":0} 确认接收。请求头与请求体
Content-Type: application/json X-Nebula-Timestamp: 1780000000 X-Nebula-Signature: HMAC-SHA256(secret, timestamp + "." + rawBody)
{
"instance_guid": "inst_xxxx",
"events": [
{
"id": 1013420,
"messageType": 0,
"contentType": 0,
"fromUserId": 7881300000000002,
"sendTime": 1789093735,
"content": [{ "type": 0, "text": "您好" }]
}
]
}
验证签名
用你的回调密钥(whsec_…,在「实例与回调」查看)按下式计算,与 X-Nebula-Signature 比对一致即为可信:
import hmac, hashlib sig = hmac.new(secret.encode(), (timestamp + ".").encode() + raw_body, hashlib.sha256).hexdigest() # sig == 请求头 X-Nebula-Signature ?
回调结构说明
了解星云 API 实时回调的事件类型、公共字段、content 数据结构和处理方式。
30 秒理解回调
你的服务器收到平台 POST 后,按这条链路处理即可,不必读完整页:
X-Nebula-Signature 校验来源可信{instance_guid, events[]}messageType(会话类型)与 contentType(内容类型)是两个独立字段,不要混用。回调请求头 / 传输层
平台把事件带签名 POST 到你在「实例与回调」配置的地址。请求头与请求体如下(Headers / Body 切换,可一键复制):
Content-Type: application/json X-Nebula-Timestamp: 1789200000 X-Nebula-Signature: <HMAC-SHA256(secret, timestamp + "." + rawBody)> User-Agent: Nebula-Webhook/1.0
{
"instance_guid": "inst_xxxxxxxx",
"events": [
{
"id": 1013420,
"messageType": 0,
"contentType": 0,
"roomId": 0,
"fromUserId": 7881300000000002,
"toUserId": 1688850000000001,
"sendTime": 1789093735,
"content": [{ "type": 0, "text": "您好" }]
}
]
}
{"code":0} 确认接收。同一次 POST 的 events[] 可含多条事件,需遍历处理。instance_guid + events[] + X-Nebula-Signature)。平台内部从上游服务包接收事件(X-Wework-* 等)属平台内部链路,不会出现在你收到的请求里。验证签名
用回调密钥(whsec_…,在「实例与回调」查看)按下式计算,与 X-Nebula-Signature 比对一致即可信:
import hmac, hashlib sig = hmac.new(secret.encode(), (timestamp + ".").encode() + raw_body, hashlib.sha256).hexdigest() # sig == 请求头 X-Nebula-Signature ?(raw_body 用原始字节,勿重序列化)
消息公共结构
events[] 里每条事件的通用字段(真实脱敏样本):
{
"id": 1013420,
"messageType": 0,
"contentType": 0,
"roomId": 0,
"fromUserId": 7881300000000002,
"toUserId": 1688850000000001,
"senderName": "",
"summary": "您好",
"sendTime": 1789093735,
"syncKey": 12014383,
"flag": 16777216,
"appInfo": "<appInfo>",
"extraData": "",
"devInfo": 0,
"content": [{ "type": 0, "text": "您好" }]
}
| 字段 | 类型 | 是否必有 | 说明 / 注意事项 |
|---|---|---|---|
| id | int64 | 是 | 消息服务端 id(撤回用 serverMsgId);同账号内唯一,可去重。 |
| messageType | int | 是 | 会话类型:0 好友(单聊) / 1 群聊 / 3 系统通知。见下节。 |
| contentType | int | 是 | 内容类型码,决定 content 形状与语义。全表见「事件目录」。 |
| roomId | int64 | 是 | 群会话 id;非群(单聊/系统私发)为 0。 |
| fromUserId | int64 | 是 | 发送方 id。群消息为真实成员 id;系统通知常为系统伪用户(如 10030 / 10014 / 10120,非真实账号)。 |
| toUserId | int64 | 是 | 接收方 id,通常是本账号;群/广播类系统通知为 0。 |
| senderName | string | 否 | 发送方昵称;实测多为空串(需昵称走通讯录接口)。 |
| summary | string | 否 | 人类可读摘要(如 [图片] / [语音] / 文章标题)。媒体/系统消息可据此直接展示。 |
| sendTime | int64 | 是 | 发送时间(秒级 Unix)。 |
| syncKey | int64 | 是 | 同步游标,递增;可作 message/sync 的位置标记。 |
| flag | int64 | 否 | 内部标志位(位掩码),原样透传即可。 |
| appInfo / extraData / devInfo | — | 否 | 平台内部透传字段,一般无需解析,原样透传。 |
| content | array/object | 是 | 消息内容,四种结构见下节。 |
Number 解析。messageType · 会话类型
好友 / 单聊
roomId=0;fromUserId 为对方 uin。
群聊
roomId 为群 id(>0);fromUserId 为群内真实发送成员。
系统通知
群相关带群 id;全局类 fromUserId 为系统伪用户(10030、10004 等)。
messageType 只区分会话;具体是文本/图片/系统事件由 contentType 决定。二者不可混用。content 的四种结构
content 不能用单一结构体解析,按 contentType 走以下四种:
A · 文本数组 contentType 0 / 2
适用:纯文本。正文在 content[].text,按段拼接得完整正文。每段的 type:0 普通文本、3 表情(text 形如 [微笑],可直接拼接)、5 @提及(text 为昵称、不含「@」,拼接时自己补「@」;带 userId;没有 userId 且 text 为「所有人」= @所有人)、10 @提及(编码版,拼接时要跳过,见下)。
"content": [{ "type": 0, "text": "就仅仅" }]
messageType:1 的群聊文本),总排在 content 第一段,后面跟一段 type 0 文本,文本里含「@昵称」(多数在开头,也可能在中间)。它的 text 不是文字,而是 Base64 编码的数据——拼接正文时请跳过 type 10,否则正文开头会多出一串乱码;「@昵称」已包含在后面的 type 0 段里。需要被 @ 人的 userId 时再解码:Base64 → protobuf,字段 1 = 被 @ 人 userId(16 位数字,内部成员 1688… / 外部微信 7881… 均可能)、字段 2 = 被 @ 人昵称。下例解码结果为 userId 7881300000000002、昵称「小明」。"content": [ { "type": 10, "text": "CIKQ+baCgIAOEgblsI/mmI4=" }, { "type": 0, "text": "@小明 明天上午开会" } ]
import base64 def text_of(content): # 拼接正文:跳过 type 10;type 5 补「@」 out = [] for seg in content: t, text = seg.get("type"), seg.get("text", "") if t == 10: continue out.append("@" + text if t == 5 else text) return "".join(out) def decode_mention(text): # 解码 type 10 → {userId, name} b, i, out = base64.b64decode(text), 0, {} def varint(): nonlocal i v = shift = 0 while True: x = b[i]; i += 1 v |= (x & 0x7F) << shift; shift += 7 if not x & 0x80: return v while i < len(b): key = varint() if key == 0x08: # 字段 1:被 @ 人 userId out["userId"] = varint() elif key == 0x12: # 字段 2:被 @ 人昵称 n = varint(); out["name"] = b[i:i + n].decode(); i += n else: break return out
B · 媒体对象 图片 / 视频 / 文件 / 语音
适用:媒体消息。id(或 url)+ aesKey 是下载凭证,配合下方 API 取原文。
"content": { "id": "<下载凭证>", "md5": "<md5>", "aesKey": "<aesKey>", "size": 1209017 }
C · 系统 hex 对象 多数系统通知 / 表情 / 图文
适用:系统信号与部分内容。hex 是十六进制 protobuf(非 base64),很多系统通知 hex 为空串,只作信号——收到即调对应同步接口。msgType == 外层 contentType。
"content": { "hex": "", "msgType": 2131 }
D · 结构化明文对象 好友申请 / 链接卡片
适用:直接给明文字段,无需解码,字段即可用。
"content": { "uin": 7881300000000002, "corpName": "示例网络科技", "customerName": "示例客户" }
contentType 事件目录
搜索或筛选你关心的事件;每条给出会话类型、content 形状、验证状态与「收到后动作」。
| contentType | 名称 | messageType | content 形状 | 验证状态 | 收到后动作 |
|---|---|---|---|---|---|
| 0 | 文本 | 0 / 1 | A 文本数组 | ✓ 已实测 | 取 content[].text 拼接(跳过 type 10) |
| 2 | 文本(变体) | 0 / 1 | A 文本数组 | ✓ 已实测 | 同 0 |
| 13 | 链接 / 视频号卡片 | 0 / 1 | D 结构化 | ✓ 已实测 | 取 title/description/imageUrl/url |
| 14 | 图片(群) | 1 | B 媒体 | ✓ 已实测 | id+aesKey → cdn/download |
| 101 | 图片(单聊) | 0 | B 媒体 | ✓ 已实测 | url+wechatAuthKey → cdn/urlDownload |
| 15 | 文件 | 0 / 1 | B 媒体 | ✓ 已实测 | cdn/download |
| 20 | 文件(转发 / 群) | 1 | B 媒体 | ✓ 已实测 | cdn/download |
| 16 | 语音 | 0 / 1 | B 媒体 | ✓ 已实测 | cdn/download(silk) |
| 23 | 视频 | 0 / 1 | B 媒体 | ✓ 已实测 | cdn/download(忽略发送端 path) |
| 103 | 视频(单聊) | 0 | B 媒体 | ✓ 已实测 | url → cdn/urlDownload |
| 104 | 动画表情 | 0 / 1 | C hex | ✓ 已实测 | hex 内含 CDN url,需解析 |
| 31 | 图文 / 公众号文章卡片 | 1 / 3 | C hex | ✓ 已实测 | summary=标题可直接展示 |
| 105 | 系统图文(如「一周小结」) | 3 | C hex | ✓ 已实测 | summary=标题 |
| 10 | 企业邮箱通知(如「邮件使用小结」) | 3 | C hex | ✓ 已实测 | summary 可直接展示,一般忽略 |
| 6 | 位置 | 0 / 1 | D 结构化 | — | 取经纬度 / 地址 |
| 41 | 名片 | 0 / 1 | D 结构化 | — | 取字段 |
| 78 | 小程序 | 0 / 1 | D 结构化 | — | 取字段(两层) |
| 26 | 红包 | 0 / 1 | C hex | — | 原样透传 |
| 2001 | 已读回执 | 0 / 1 | C hex(短) | ✓ 已实测 | 发消息后必收一条,可忽略 |
| 2131 | 外部联系人信息变动 / 删除 | 3 | C hex(常空) | ✓ 已实测 | 调 contact/syncExternal |
| 2357 | 好友申请(结构化) | 3 | D 结构化 | ✓ 已实测 | 展示申请,agreeToNewCustomer 同意 |
| 2132 | 好友申请 | 3 | C hex(常空) | ✓ 已实测 | 同上 |
| 2118 | 群信息变动 | 1 | C hex(常空) | ✓ 已实测 | 按 roomId 刷新群资料 |
| 1006 | 群成员 / 建群相关 | 1 | C hex(成员id) | ✓ 已实测 | 刷新群成员 |
| 2104 | 联系人免打扰 / 置顶 | 3 | C hex | ✓ 已实测 | 原样透传 |
| 2130 | 系统通知(语义待定) | 3 | C hex(空) | ✓ 已实测 | 原样透传 |
| 2180 | 系统通知(语义待定) | 3 | C hex(空) | ✓ 已实测 | 原样透传 |
| 2201 | 系统通知(语义待定) | 3 | C hex(空) | ✓ 已实测 | 原样透传 |
| 2186 | 个人标签变更 | 3 | C hex | — | 调 label/sync(syncType=2) |
| 2185 | 企业标签变更 | 3 | C hex | — | 调 label/sync(syncType=1) |
| 2188 | 内部联系人变动 | 3 | C hex | — | 调通讯录同步 |
| 2063 | 撤回消息 | 0 / 1 | C hex | ✓ 已实测 | 解 hex 字段 15(=原消息 appInfo)定位原消息,标记已撤回 |
真实消息示例(脱敏)
生产真实报文,已脱敏(id/uin/roomId/md5/aesKey/URL 凭证/姓名替换为占位)。常用类型直接展示,其余折叠。
文本(contentType 0 / 2)
{ "id": 1000554, "messageType": 1, "contentType": 0, "roomId": 10000000000002,
"fromUserId": 1688850000000011, "sendTime": 1789106713,
"content": [{ "type": 0, "text": "就仅仅" }] }处理:取 content[].text 拼接得完整正文(跳过 type 10 段,见上方「A · 文本数组」)。contentType 0 和 2 都是文本,单聊(messageType:0、roomId:0)和群聊(messageType:1)里都会出现;区分单聊 / 群聊请看 messageType / roomId,不要用 contentType 判断。
图片(contentType 14 群 / 101 单聊)
{ "id": 1013724, "messageType": 1, "contentType": 14, "roomId": 10000000000002,
"content": { "id": "<下载凭证>", "md5": "<md5>", "aesKey": "<aesKey>",
"size": 1209017, "width": 1080, "height": 1920, "thumbMd5": "<md5>" } }处理:14(群)用 id+aesKey 走 cdn/download;101(单聊)content 带 url+wechatAuthKey,走 cdn/urlDownload。
语音(contentType 16)
{ "id": 1013637, "messageType": 0, "contentType": 16, "summary": "[语音]",
"content": { "id": "<下载凭证>", "md5": "<md5>", "aesKey": "<aesKey>", "size": 2608, "voiceTime": 2 } }处理:voiceTime 秒;silk 格式,走 cdn/download。
视频(contentType 23 / 103)
{ "id": 1013739, "messageType": 0, "contentType": 23,
"content": { "id": "<下载凭证>", "md5": "<md5>", "aesKey": "<aesKey>",
"size": 2533630, "width": 1920, "height": 1080, "duration": 2 } }处理:23 走 cdn/download(忽略发送端本地 path);103(单聊)带 url,走 cdn/urlDownload。
文件(contentType 15 / 20)
{ "id": 1013745, "messageType": 0, "contentType": 15,
"content": { "id": "<下载凭证>", "md5": "<md5>", "aesKey": "<aesKey>", "name": "示例文件.jpg", "size": 1863186 } }处理:走 cdn/download;20(群/转发)同构,summary=发送人 : [文件名]。
{ "id": 1013605, "messageType": 0, "contentType": 104, "summary": "[动画表情]",
"content": { "hex": "0a4768747470…(十六进制,内含 CDN url 与表情信息)", "msgType": 104 } }处理:C 结构,hex 为十六进制 protobuf,解出内含 CDN url。
{ "id": 1013415, "messageType": 3, "contentType": 31, "roomId": 10120,
"summary": "同事们在看《示例文章标题》",
"content": { "hex": "0aeb020a…(十六进制,含标题/摘要/落地页/配图)", "msgType": 31 } }处理:summary 即文章标题可直接展示;明细需解 hex。
{ "id": 1001089, "messageType": 1, "contentType": 13, "roomId": 10000000000002,
"content": { "title": "卡片标题", "description": "卡片描述",
"imageUrl": "https://example.com/cover.jpg", "sph_feed_h5_message": { "url": "<base64>" } } }处理:普通链接取 title/description/imageUrl/url;视频号在 sph_feed_h5_message(字段 base64)。
{ "id": 1006265, "messageType": 1, "contentType": 2063, "roomId": 10000000000002,
"fromUserId": 1688850000000001, "appInfo": "<本条撤回通知自身的标识>",
"content": { "hex": "180020…(十六进制 protobuf,内嵌被撤回的原消息)", "msgType": 2063 },
"extraData": "<base64,内含原消息标识>" }hex 按 protobuf 解码后的关键字段(字段号 → 含义):
| 字段号 | 含义 |
|---|---|
| 4 | 原消息发送者 userId(即撤回人) |
| 5 | 原消息接收方 userId(单聊时) |
| 7 | 原消息的 contentType(如 0 文本、102 文件) |
| 8 | 原消息完整内容(嵌套 protobuf)。文本:正文在 8 → 2 → 1;文件:2 文件名、3 下载链接、4 大小(字节)、8/10 md5、25 fileId |
| 12 | 原消息发送时间(秒级时间戳) |
| 15 | 原消息的 appInfo——与之前收到那条消息的 appInfo 完全一致,用它定位被撤回的消息 |
处理:按字段 15 找到原消息并标记「已撤回」;不要当成新消息展示,也不要再展示或下载被撤回的内容。单聊(messageType 0)与群聊(1)都会推送;用 message/revoke 撤回自己发的消息(serverMsgId 取该消息的 id)同样会收到本回调。已实测文本、文件撤回,结构一致。
系统通知 · 收到后动作
系统通知多为 content:{hex,msgType} 且 hex 常空——它们是信号,重点是「收到后调哪个接口」:
| contentType | 事件 | 收到后动作 |
|---|---|---|
| 2001 | 已读回执 | 发消息后必收一条,一般忽略 |
| 10 | 企业邮箱通知(如「查看企业本周的邮件使用小结(9.22-9.28)」、邮箱开通推广) | summary 即通知文字,可直接展示;一般忽略。fromUserId / roomId 为 10004 |
| 2131 | 外部联系人信息变动 / 删除 | 调 contact/syncExternal 拉增量 |
| 2357 / 2132 | 好友申请(结构化 / 信号) | 2357 直接给明文可展示;agreeToNewCustomer 同意 |
| 2118 | 群信息变动 | 按 roomId 刷新群资料(频率最高的群推送) |
| 1006 | 群成员 / 建群 | 刷新群成员(hex 解出为分号连接的成员 uin) |
| 2186 | 个人标签变更 | 调 label/sync(syncType=2) |
| 2185 | 企业标签变更 | 调 label/sync(syncType=1) |
| 2063 | 撤回消息 | 解 hex 字段 15 得原消息 appInfo,据此找到原消息标记「已撤回」(详见上方「2063 撤回消息」示例) |
{ "id": 1013589, "messageType": 3, "contentType": 2357, "roomId": 10030, "fromUserId": 10030,
"summary": "示例客户@微信申请添加你为联系人",
"content": { "uin": 7881300000000002, "corpId": 1970320000000001,
"source": "微信", "corpName": "示例网络科技", "customerName": "示例客户" } }fromUserId 为 10030、10004 等是系统伪用户(非真实账号);hex 空只作信号。连接事件
长链状态变化也会进入 events[]。你侧按 body 形状识别(你收不到平台内部的事件名头):
| 事件 | 你收到的 body | 语义 |
|---|---|---|
| 连接建立 / 已连接 | 空事件 {} —— 平台已自动过滤,不再转发给你 | 无需处理 |
| 长链断开 | { "code":0, "message":"手动关闭长链", "time":"…" } | 主动关闭(code=0)等;code 见错误码 |
哪些操作会触发回调
实测结论——经接口发送消息不触发回调(只会其后一条 2001 回执):
| 操作 | 是否推送 |
|---|---|
| 经接口发消息 | 不推,仅其后一条 contentType 2001 回执 |
| 客户端手动发消息 | 推(文本 / 图片 / 语音 / 视频 / 文件 / 表情…均已抓到) |
| 收到好友申请 | 推 2357(结构化)/ 2132 |
| 外部联系人变动 | 推 2131 → 调 syncExternal |
| 群设置变更 | 推 若干 2118 + 具体事件 |
| 大文件上传完成 | 推 上传完成事件(异步) |
| 账号被别处登录踢下线 | 不推(见连接事件说明) |
解析注意与安全
- int64 精度:超过 2⁵³ 的大整数平台已转字符串;统一按字符串 / BigInt 处理,勿用
Number。 - hex ≠ base64:系统类
content.hex是十六进制 protobuf(常为空),不要当 base64 解。 - 回调 与 message/sync 不同源:回调的文本/媒体/结构化类可直接拿明文/凭证;
message/sync返回的是企微 protobuf——两者不可用同一解析器。 - 媒体凭证(
id/aesKey/ 下载 url 临时票据)时效性强,不要长期明文保存,即用即取。 - 示例已脱敏:本页 id / 姓名 / 企业名 / md5 / aesKey / URL 凭证均为占位,真实值以你收到的为准。
遇到问题?在控制台「联系客服」或添加客服微信 xingyunbot 获取技术支持。
错误码
调用 API 时,如果返回的 code 不为 0,表示请求失败。你可以根据错误码定位问题并采取相应措施。
| 错误码 | 类型 | 描述 | 解决方案 |
|---|---|---|---|
| 0 | 成功 | 请求成功,业务数据在 data 字段 | 无 |
| 401 | 认证相关 | API Key 无效或缺失 | 请求头带 X-Nebula-Key: <你的Key>,值取自控制台「API Key」页 |
| 40001 | 请求参数 | 请求体必须为 JSON 对象 | Content-Type 用 application/json,body 为合法 JSON |
| 40301 | 接口限制 | 该接口需申请企业 POC 后开放(体验版仅开放 20 个基础接口) | 在控制台「企业 POC」提交申请,审核通过后全部接口开放 |
| 40302 | 接口限制 | 账号额度已满,无法再接入新实例 | 申请企业 POC 或联系客服扩容 |
| 42901 | 接口限制 | 同一账号同时进行的上传/下载超过 2 个,排队 60 秒仍未轮到(HTTP 429) | 稍后重试;同一账号的上传/下载请逐个或少量进行,不要一次并发很多个 |
| 50001 | 业务错误 | 接口调用失败,具体原因见 message(如「conversationId is required」) | 按 message 提示检查参数、实例是否在线 |
| 50000 | 服务器错误 | 上游服务调用失败(超时 / 网络异常 / 暂不可用) | 请稍后重试,持续失败请联系客服 |
| 50002 | 服务器错误 | 创建实例成功但未获取到实例标识 | 请重试,或联系客服 |