从创建应用到真实调用
创建应用
控制台创建应用,保存 AppKey、AppSecret 和回调地址。
扫码授权
把授权地址放到登录按钮,用户在 Q助理 App 扫码确认。
换 token 并调用
服务端换取用户或应用 token,再调用对应接口。
keyAppSecret 仅在创建或重置时完整展示一次,请妥善保存并仅在服务端使用。
请求头与统一响应
服务端调用 POST https://open.qzhuli.com/oauth/access_token 和业务 API 时需要以下请求头。业务 API 还需要 Authorization: Bearer ACCESS_TOKEN。
| 请求头 | 说明 |
|---|---|
X-QZ-App-Key | 控制台生成的 AppKey |
X-QZ-Timestamp | Unix 毫秒时间戳,允许与服务端相差约 5 分钟 |
X-QZ-Nonce | 每次请求新生成的 16–64 位随机串 |
X-QZ-Sign | 按签名规则生成的小写 HMAC-SHA256 |
{
"code": 200,
"msg": "获取成功",
"data": { "...": "业务数据" }
}POST 使用 JSON body,GET 使用 query。列表返回 items、total、page、page_size;时间为 Unix 秒,金额为分。
签名规则
浏览器跳转 GET https://open.qzhuli.com/oauth/authorize 不签名;其余对外服务端接口均须签名。每次请求使用新的 nonce,且 nonce 为 16–64 位 A-Za-z0-9_- 随机串。
- 收集 body 或 query 的全部业务字段。
POST https://open.qzhuli.com/oauth/access_token的app_secret仅用于凭证校验,不参与签名;业务字段不得使用app_key、timestamp、nonce、access_token、sign。 - 加入请求头的
app_key、timestamp、nonce;业务 API 还加入 Bearer token 作为access_token。 - 字段名按 ASCII 升序排序。字符串原样、整数十进制、布尔值为
true/false;字符串数组保持顺序并编码为无空格 JSON。字段名和值按 RFC 3986 编码后以key=value&...拼接。 - 以
UPPERCASE_METHOD + "\n" + PATH + "\n" + 规范字段串为待签名文本,使用 AppSecret 的原始 UTF-8 值计算 HMAC-SHA256,输出小写 hex。
POST /oauth/access_token app_key=YOUR_APP_KEY&grant_type=client_credentials&nonce=RANDOM_NONCE_AT_LEAST_16×tamp=UNIX_MS_TIMESTAMP
时间戳允许与平台相差约 5 分钟;同一 AppKey 下 nonce 只能使用一次。业务接口签名时,access_token 必须与 Authorization: Bearer ... 中的值完全一致。
扫码授权
授权地址由你的应用生成 state 并保存到服务端会话。开启 PKCE 时同时保存 code_verifier,仅把 challenge 放进地址。
https://open.qzhuli.com/oauth/authorize?app_key=YOUR_APP_KEY&redirect_uri=https%3A%2F%2Fexample.com%2Foauth%2Fcallback&state=RANDOM_STATE&scope=user_info&code_challenge=PKCE_CHALLENGE&code_challenge_method=S256
平台会展示二维码并完成扫码和授权。用户同意后回调 redirect_uri?code=...&state=...;用户拒绝则回调 error=access_denied&state=...。若应用开启手机号授权,用户必须明确同意;拒绝则本次应用授权不完成。
| 接口 | 用途 |
|---|---|
GET https://open.qzhuli.com/oauth/authorize | 发起扫码授权并处理授权回调 |
服务端换 token
换码请求在服务端执行,不能把 AppSecret 放在浏览器。AppKey 放在 X-QZ-App-Key 请求头,body 示例:
{
"app_secret": "YOUR_APP_SECRET",
"grant_type": "authorization_code",
"code": "ONE_TIME_CODE",
"redirect_uri": "https://example.com/oauth/callback",
"code_verifier": "PKCE_VERIFIER"
}{ "code": 200, "data": {
"access_token": "ou_...", "refresh_token": "or_...",
"token_type": "Bearer", "credential_type": "user",
"expires_in": 7200, "user": { "q_uid": "q_..." }
} }应用服务端令牌也可用 grant_type=client_credentials 换取,用于应用级消息、统计和控制台以外的业务调用。
用户信息
使用用户 access_token,body 传该 token 对应的 q_uid。手机号只在应用开启手机号、用户本次同意且授权版本有效时返回;其他情况不会返回该字段。
{ "code": 200, "data": {
"q_uid": "q_...", "nickname": "用户昵称", "avatar": "https://...",
"is_connected_agent": true, "phone": "13800000000"
} }消息与统计
以下接口使用 grant_type=client_credentials 换得的应用 access token,并在请求头携带 Authorization: Bearer APP_ACCESS_TOKEN。除特别说明外,所有时间字段均为 Unix 秒时间戳,read_rate 返回 0–1 的小数。
单个消息推送
向一个已完成应用授权、且已连接对应数字员工的私域用户发送消息。接口会先记录消息批次,再执行 IM 推送和可选的短信兜底。
| Body 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
q_uid | string | 是 | 目标用户的 Q UID。 |
message | string | 是 | 消息正文;不能为空,最多 10,000 个字符且不超过 60,000 字节。 |
title | string | 否 | 通知标题,最多 100 个字符;不传时使用数字员工名称生成默认标题。 |
url | string | 否 | 点击消息后的跳转地址,最多 2,048 字节;默认 qzhuli://open-platform/message。 |
message_id | string | 否 | 业务幂等 ID,1–64 位,仅允许 A-Z a-z 0-9 . _ ~ -。同一 App 下相同 ID 必须使用相同消息内容。 |
message_importance | integer | 否 | 消息重要级别 1、2 或 3,默认 1。 |
sms_fallback_enabled | integer | 否 | 是否启用短信兜底,0/1,默认 0;启用时 message_importance 必须不低于 2。 |
timeout_seconds | integer | 否 | 等待 IM 已读后触发兜底的秒数,范围 60–86,400,默认 60。 |
POST /open/message/push
Content-Type: application/json
Authorization: Bearer APP_ACCESS_TOKEN
{
"q_uid": "q_...",
"title": "订单提醒",
"message": "你的订单已发货",
"url": "https://example.com/orders/123",
"message_id": "order_123",
"message_importance": 2,
"sms_fallback_enabled": 1,
"timeout_seconds": 300
}{
"code": 200,
"msg": "发送成功",
"data": {
"message_id": "om_01H...",
"client_message_id": "order_123",
"status": "sent"
}
}info单个推送成功响应只返回批次 ID、幂等 ID 和当前状态;如果用户未授权或数字员工未连接,会返回错误码,并在 data.message_id 中保留已创建的消息批次 ID。
群发消息
向多个私域用户创建一个群发批次。服务端会去重 q_uid_list,每个收件人分别记录投递结果,最多支持 1,000 个用户。
| Body 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
q_uid_list | string[] | 是 | 目标用户 Q UID 数组,去重后数量必须为 1–1,000。 |
message | string | 是 | 消息正文;不能为空,最多 10,000 个字符且不超过 60,000 字节。 |
title | string | 否 | 通知标题,最多 100 个字符;不传时按数字员工名称生成。 |
url | string | 否 | 跳转地址,最多 2,048 字节;默认 qzhuli://open-platform/message。 |
message_id | string | 否 | 业务幂等 ID,规则同单个推送;相同 ID 不能复用到不同内容或收件人。 |
message_importance | integer | 否 | 消息重要级别 1、2 或 3,默认 1。 |
sms_fallback_enabled | integer | 否 | 是否启用短信兜底,0/1,默认 0;启用时重要级别至少为 2。 |
timeout_seconds | integer | 否 | 兜底等待时长,范围 60–86,400,默认 60。 |
{
"code": 200,
"msg": "发送完成",
"data": {
"message_id": "om_01H...",
"client_message_id": "campaign_20260917",
"status": "partial",
"title": "活动通知",
"message": "活动开始了",
"url": "qzhuli://open-platform/message",
"total_count": 2,
"sent_count": 1,
"read_count": 0,
"failed_count": 1,
"read_rate": 0,
"submitted_time": 1770000000
}
}批次状态为 queued、sent、partial 或 failed;群发接口返回的计数分别表示收件人总数、已发送数、已读数和失败数。
消息发送记录
分页读取当前应用创建的消息批次。读取时会同步已读回执,因此返回的批次状态和计数可能比上一次查询更新。
| Query 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | integer | 否 | 页码,从 1 开始,默认 1,最大 1000。 |
page_size | integer | 否 | 每页数量,默认 20,最大 100。 |
{
"code": 200,
"msg": "获取成功",
"data": {
"items": [{
"message_id": "om_01H...",
"client_message_id": "order_123",
"status": "sent",
"title": "订单提醒",
"message": "你的订单已发货",
"url": "qzhuli://open-platform/message",
"total_count": 1,
"sent_count": 1,
"read_count": 1,
"failed_count": 0,
"read_rate": 1,
"submitted_time": 1770000000,
"recipient_type": "single",
"recipient_name": "张三",
"recipient_count": 1,
"uid": "u_...",
"app_id": "app_...",
"app_name": "订单助手"
}],
"page": 1,
"page_size": 20,
"total": 1
}
}recipient_type 为 single 或 broadcast;群发记录的 uid 和单个收件人名称可能为空,详情请调用已读状态接口。
消息已读状态
读取一个消息批次下每个收件人的投递和已读状态,也可用于查询群发的失败原因。
| Body / Query 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
message_id | string | 是 | 消息批次 ID,即发送接口返回的 data.message_id。 |
page | integer | 否 | 收件人页码,从 1 开始,默认 1,最大 1000。 |
page_size | integer | 否 | 每页收件人数,默认 100,最大 100。 |
{
"message_id": "om_01H...",
"page": 1,
"page_size": 100
}{
"code": 200,
"msg": "获取成功",
"data": {
"message_id": "om_01H...",
"client_message_id": "campaign_20260917",
"title": "活动通知",
"message": "活动开始了",
"url": "qzhuli://open-platform/message",
"status": "sent",
"total_count": 1,
"sent_count": 1,
"read_count": 1,
"failed_count": 0,
"read_rate": 1,
"submitted_time": 1770000000,
"recipients": [{
"q_uid": "q_...",
"uid": "u_...",
"recipient_name": "张三",
"status": "read",
"sent_time": 1770000010,
"read_time": 1770000040,
"sms_status": "disabled",
"reason": "",
"error_message": ""
}],
"recipient_page": 1,
"recipient_page_size": 100
}
}响应中的批次字段与发送记录一致;recipients 是收件人明细,sent_time 和 read_time 为 Unix 秒时间戳。收件人 status 可能为 queued、sending、sent、read 或 failed;失败时查看 reason 和 error_message。
统计概览
返回当前应用的授权、连接和消息投递汇总,不需要额外 query 参数。
{
"code": 200,
"msg": "获取成功",
"data": {
"login_count": 128,
"active_private_users": 96,
"new_connections": 24,
"today_new_connections": 5,
"sent_count": 320,
"read_count": 256,
"read_rate": 0.8
}
}| 字段 | 说明 |
|---|---|
login_count | 当前有效授权用户数,同一应用用户只计一次。 |
active_private_users | 当前有效的数字员工连接数。 |
new_connections | 最近 30 天内建立连接的数量。 |
today_new_connections | 今天首次授权的用户数。 |
sent_count | 按逻辑消息去重后的发送总数。 |
read_count | 已收到真实已读回执的消息数。 |
read_rate | 已读数 / 发送数,范围 0–1;没有发送记录时为 0。 |
私域用户列表
分页读取当前应用的有效私域用户。列表只返回脱敏手机号;如需在授权范围内读取完整手机号,应使用对应用户 access token 调用用户信息接口。
| Query 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | integer | 否 | 页码,从 1 开始,默认 1。 |
page_size | integer | 否 | 每页数量,默认 20,最大 100。 |
{
"code": 200,
"msg": "获取成功",
"data": {
"items": [{
"q_uid": "q_...",
"name": "张三",
"phone_masked": "138****0000",
"phone_authorized": true,
"connection_status": "connected",
"status": 1,
"last_authorize_time": 1770000000
}],
"page": 1,
"page_size": 20,
"total": 128
}
}| 字段 | 说明 |
|---|---|
q_uid | 开放平台用户标识。 |
name | 用户昵称,找不到用户时为空字符串。 |
phone_masked | 脱敏手机号,例如 138****0000;未授权时为空。 |
phone_authorized | 当前应用授权范围内是否有可用手机号。 |
connection_status | connected 表示已连接数字员工,authorized 表示已授权但尚未连接。 |
status | 授权记录状态;此列表只返回有效记录 1。 |
last_authorize_time | 最近一次授权时间,Unix 秒时间戳。 |
错误处理
不能只判断 HTTP 200:业务错误可能仍返回 HTTP 200,必须同时检查 code;网关错误还可能返回 4xx/5xx。
| code | 处理建议 |
|---|---|
40001 | 参数、授权范围或版本冲突;修正请求后重试 |
40002 | 缺少/无效签名、会话或企业上下文;重新读取会话或检查请求头 |
40003 | access_token 无效/过期;按授权流程重新换 token |
40004 | 一次性 code 已使用或已过期;重新发起扫码授权 |
40005 | 数字员工尚未连接;确认用户已完成应用授权且仍为有效私域用户 |
40006 | 消息参数或目标不可用;不要伪造成功记录,展示服务端 msg |
429/5xx | 按退避重试;幂等写请求沿用同一业务 message_id |