从创建应用到真实调用
创建应用
控制台创建应用,保存 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。
| 接口 | 请求 | 说明 |
|---|---|---|
POST https://open.qzhuli.com/open/message/push | q_uid、message、可选 title/url/message_id/message_importance/sms_fallback_enabled/timeout_seconds | 单个已授权用户发送,返回 message_id 与投递状态 |
POST https://open.qzhuli.com/open/message/broadcast | q_uid_list(1–1000)及消息字段 | 群发,返回成功/失败数量 |
GET https://open.qzhuli.com/open/message/records | page,page_size | 真实发送记录分页 |
POST https://open.qzhuli.com/open/message/read_status | message_id,可选 page,page_size | 查询真实已读状态 |
GET https://open.qzhuli.com/open/stats/overview | 无业务参数 | 授权、连接、发送、已读率统计 |
GET https://open.qzhuli.com/open/stats/private_users | page,page_size | 私域用户真实分页,列表仅返回脱敏手机号 |
{ "q_uid": "q_...", "title": "订单提醒", "message": "你的订单已发货", "message_id": "order_123" }错误处理
不能只判断 HTTP 200:业务错误可能仍返回 HTTP 200,必须同时检查 code;网关错误还可能返回 4xx/5xx。
| code | 处理建议 |
|---|---|
40001 | 参数、授权范围或版本冲突;修正请求后重试 |
40002 | 缺少/无效签名、会话或企业上下文;重新读取会话或检查请求头 |
40003 | access_token 无效/过期;按授权流程重新换 token |
40004 | 一次性 code 已使用或已过期;重新发起扫码授权 |
40005 | 数字员工尚未连接;确认用户已完成应用授权且仍为有效私域用户 |
40006 | 消息参数或目标不可用;不要伪造成功记录,展示服务端 msg |
429/5xx | 按退避重试;幂等写请求沿用同一业务 message_id |