通用说明
服务端 API 接入、签名校验方式和通用错误码。
服务端 API 面向后端系统对接,用于把企业自有系统与七鱼、云商知识库等能力连接起来。开始开发前,先确认接口的调用方向和业务分组,再按对应场景准备凭证、签名或 token。
由于调用场景不同,以及对于安全性要求的提高,七鱼系统目前存在以下几种常见签名校验方式。
| 调用方向 | 校验方式 | 适用场景 | 凭证/签名位置 |
|---|---|---|---|
| 企业服务器 -> 七鱼服务器 | 七鱼签名 | 员工管理、客户管理、工单、短信、企微等通用七鱼 OpenAPI | appKey、time、checksum 和可选 checksumAlgorithm 放在 query 中 |
| 企业服务器 -> 云商服务器 | 云商签名 | 知识库 | X-YS-APIKEY、X-YS-TIME、X-YS-SIGNATURE 放在请求头中 |
| 七鱼服务器或七鱼工作台 -> 企业服务器 | 校验 token | 企业提供用户信息、订单信息等对接接口 | token 随七鱼请求传给企业接口 |
| 七鱼服务器 -> 企业服务器 | 校验签名 | 消息事件、CRM 认证、商品订单查询、工单回调、机器人动态内容等企业提供接口 | 通常为 time、nonce、checksum/checkSum 等请求参数或请求头 |
如果接口页面未特别说明使用云商签名,默认按七鱼签名接入。企业提供接口给七鱼调用时,请优先阅读对应业务文档和 企业接口接入,因为这类接口的调用方向和签名字段与企业调用七鱼 OpenAPI 不完全相同。
七鱼签名
服务端 API 中的通用七鱼 OpenAPI 使用七鱼签名。调用方需要在 query 中传入 appKey、time 和 checksum,部分接口还支持传入 checksumAlgorithm 指定最终哈希算法。七鱼服务端使用 checksum 校验调用方身份和请求完整性。
七鱼签名和七鱼调用企业接口时的签名容易混淆:
| 场景 | 调用方向 | 参与计算字段 |
|---|---|---|
| 企业调用七鱼 OpenAPI | 企业服务器 -> 七鱼服务器 | appSecret + md5 + time,其中 md5 为请求体或文件内容的 MD5 |
| 七鱼调用企业接口 | 七鱼服务器 -> 企业服务器 | 常见为 appSecret + nonce + time,其中 nonce 为七鱼生成的随机字符串 |
数据校验
企业服务器调用七鱼 OpenAPI 时,POST 请求需要在 query 中带以下参数:
| 参数 | 参数类型 | 参数说明 | 是否必填 |
|---|---|---|---|
| appKey | String | 企业的 appKey,可在七鱼管理后台查看 | 是 |
| time | Long | 当前 UTC 时间戳,从 1970 年 1 月 1 日 0 点 0 分 0 秒开始到现在的秒数 | 是 |
| checksum | String | 默认按 SHA1(appSecret + md5 + time) 计算;当接口声明并传入 checksumAlgorithm=1 时,按 SHA-256(appSecret + md5 + time) 计算;结果转化成 16 进制字符(String,小写) | 是 |
| checksumAlgorithm | Integer | 可选签名算法参数;仅接口参数表中声明该字段时需要展示和传入。0 或不传表示 SHA1,1 表示 SHA-256。 | 否 |
其中,checksum 用于校验数据的完整性,其计算规则中,各字段取值如下:
- appSecret 可在七鱼管理后台 ->「系统」->「扩展与集成」页面找到。
- md5 为整个请求 JSON 字符串的 md5 哈希值(小写形式),即
md5 = MD5(content).toLowerCase()。如果是上传文件,则是整个文件内容的 md5。 - time 就是请求参数中的 time。
特殊说明:出于安全性考虑,每个 checksum 的有效期为 5 分钟,请确认发起请求的服务器是与标准时间同步的,比如有 NTP 服务。
计算公式:
checksum = SHA1(appSecret + md5 + time)
md5 = MD5(请求体原文).toLowerCase()
time = 秒级时间戳
如果接口声明并传入 checksumAlgorithm=1:
checksum = SHA-256(appSecret + md5 + time)API 调试窗口
每个 API 文档页右侧的调试窗口已内置七鱼签名辅助能力。开发者在调试窗口中填写 appKey 和 appSecret 后,点击「调试」时,窗口会自动写入当前秒级 time,并按请求体原文计算 md5 和 checksum,无需手动填写这两个字段。
如果接口参数中存在 checksumAlgorithm,调试窗口会读取本次请求中的该参数值:传 1 时使用 SHA-256 生成 checksum;未传、传 0 或其他值时使用默认 SHA1 生成 checksum。如果接口参数表中没有 checksumAlgorithm,调试窗口不会展示该字段,计算签名时按 0 处理。checksumAlgorithm 本身需要在参数区按接口要求填写。
以下为参考代码。
checksum 的 Java 示例代码
public class QiyuPushCheckSum {
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String encode(String appSecret, String md5, long time) {
return encode(appSecret, md5, time, 0);
}
public static String encode(String appSecret, String md5, long time, int checksumAlgorithm) {
String content = appSecret + md5 + time;
String algorithm = checksumAlgorithm == 1 ? "SHA-256" : "SHA-1";
try {
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
messageDigest.update(content.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
}MD5 计算 Java 代码示例
public class MD5 {
private final static char[] hexDigits = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static String md5(String input) {
return md5(input, 32);
}
public static String md5(String input, int bit) {
try {
MessageDigest md = MessageDigest.getInstance(System.getProperty(
"MD5.algorithm", "MD5"));
if (bit == 16)
return bytesToHex(md.digest(input.getBytes("utf-8")))
.substring(8, 24);
if (bit == 28) {
return bytesToHex(md.digest(input.getBytes("utf-8")))
.substring(2, 30);
}
return bytesToHex(md.digest(input.getBytes("utf-8")));
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
private static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
int t;
for (int i = 0; i < 16; i++) {
t = bytes[i];
if (t < 0)
t += 256;
sb.append(hexDigits[(t >>> 4)]);
sb.append(hexDigits[(t % 16)]);
}
return sb.toString();
}
}请求七鱼 OpenAPI 接口 Java 代码示例
public static void main(String[] args) {
// 请求内容,用来放到 body 体中
String content = "{\"name\":\"qiyu\",\"age\":\"8\"}";
// 对 content 进行 md5 哈希,然后转化为小写形式
String md5 = MD5.md5(content).toLowerCase();
// 取时间戳,注意是秒,不是毫秒
long time = System.currentTimeMillis() / 1000;
String appkey = "appkey";
String secret = "secret";
// 生成 checksum
String checksum = QiyuPushCheckSum.encode(secret, md5, time);
// 拼接请求 url
String url = "https://qiyukf.com/xxx";
url += "?appKey=" + appkey;
url += "&time=" + time;
url += "&checksum=" + checksum;
// 发送 POST 请求,content 放到 body 体中
String res = HttpClientPool.getInstance().postMethod(url, content);
}重要提示: 本文档中提供的所有接口均面向开发者服务器端调用。用于计算 checksum 的 AppSecret 开发者应妥善保管,可在应用的服务器端存储和使用,但不应存储或传递到客户端,也不应在网页等前端代码中嵌入。
云商签名
知识库 使用云商签名,不使用七鱼签名。调用方需要在请求头中传入云商凭证和签名。
| 请求头 | 必填 | 说明 |
|---|---|---|
X-YS-APIKEY | 是 | 云商 appKey。 |
X-YS-TIME | 是 | 时间戳字符串,按原始字符串参与签名。 |
X-YS-SIGNATURE | 是 | 使用 appSecret 计算的 HmacSHA256 签名。 |
签名原文:
needSign = timestamp + requestQuery + bodytimestamp:请求头X-YS-TIME的原始值。requestQuery:URL 中的 query string,不含?;没有 query 时为空字符串。body:原始请求体字符串;无请求体时为空字符串。
签名算法:
signature = HexLower(HmacSHA256(needSign, appSecret))知识库中 /api/athena/openApi/external/** 使用企业级云商凭证;/api/athena/agent/tool/** 使用个人 API 凭证,部分场景兼容企业级云商凭证。
云商签名 Java 示例代码
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class YsOpenApiSignature {
public static String sign(String appSecret, String timestamp, String requestQuery, String body) {
String needSign = valueOf(timestamp) + valueOf(requestQuery) + valueOf(body);
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
return toHexLower(mac.doFinal(needSign.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String valueOf(String value) {
return value == null ? "" : value;
}
private static String toHexLower(byte[] bytes) {
StringBuilder builder = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
builder.append(String.format("%02x", b & 0xff));
}
return builder.toString();
}
public static void main(String[] args) {
String appKey = "your-app-key";
String appSecret = "your-app-secret";
String timestamp = String.valueOf(System.currentTimeMillis());
String requestQuery = "spaceId=123&page=1";
String body = "{\"name\":\"example\"}";
String signature = sign(appSecret, timestamp, requestQuery, body);
// 请求时设置:
// X-YS-APIKEY: appKey
// X-YS-TIME: timestamp
// X-YS-SIGNATURE: signature
}
}校验 token
校验 token 用于部分企业提供接口、七鱼调用企业接口的场景。企业在七鱼后台配置自定义 AppID、AppSecret 以及获取 token 的接口,七鱼调用企业接口时会携带 token,企业收到请求后可以校验 token 的正确性和时效性。
配置入口如下:

目前使用 token 方式校验的主要是以下企业接口:
| 接口 | 调用说明 |
|---|---|
| 用户信息(在线)URL | 在线系统查询企业侧用户信息 |
| 用户信息(呼叫)URL | 呼叫系统查询企业侧用户信息 |
| 用户订单接口(在线)URL | 在线系统查询企业侧订单信息 |
| 用户订单接口(呼叫)URL | 呼叫系统查询企业侧订单信息 |
token 由七鱼后端调用企业提供的 token 接口获取,并根据返回时间进行缓存。前端触发上述能力时,会先通过七鱼封装的后端接口获取 token,再调用企业业务接口获取用户信息或订单信息。
注意:当获取 token 接口异常或超时时,七鱼会直接使用 AppSecret 作为 token 调用企业业务接口。企业实现校验 token 时,需要考虑这个兜底行为。
校验签名
校验签名用于七鱼调用企业提供 HTTP 接口的场景。企业在七鱼后台配置接口地址和 AppSecret,七鱼发起请求时携带时间戳、随机串和签名,企业侧用同一份 AppSecret 重新计算签名并比对。
最常见的签名规则如下:
checkSum = SHA1(appSecret + nonce + time)
nonce = 七鱼生成的随机字符串
time = 秒级时间戳其中 appSecret 通常是企业在七鱼后台对应接入模块中配置的 AppSecret,nonce 不是请求体 MD5。不同业务可能会把 time、nonce、checksum/checkSum 放在 query 或 header 中,字段大小写也可能不同,请以具体业务文档为准。
常见请求字段
| 参数 | 常见位置 | 说明 |
|---|---|---|
appId / appid | Header 或 Body | 企业分配给七鱼的 AppID。部分接口不传或使用小写字段名。 |
time | Header 或 Query | 当前 UTC 时间戳,通常精确到秒。 |
nonce | Header 或 Query | 七鱼生成的随机字符串,常见上限为 128 个字符。 |
checksum / checkSum | Header 或 Query | 七鱼根据约定规则计算出的签名。字段大小写以具体接口文档为准。 |
token | Header、Query 或 Body | 如果该业务同时启用了校验 token,七鱼会把获取到的 token 一并带给企业接口。 |
企业侧校验步骤
- 从请求中读取
time、nonce和checksum/checkSum。 - 使用企业在七鱼后台对应接入模块中配置的 AppSecret,按业务文档要求重新计算签名。
- 将重新计算出的签名与七鱼请求中携带的签名做字符串比较。
- 校验通过后再处理业务参数;校验失败时按业务文档约定返回错误响应。
建议企业侧同时检查 time 与当前服务器时间的偏差,避免历史请求被重放。允许的时间窗口可按业务安全要求设置,并确保服务器时间与标准时间同步。
校验签名 Java 示例代码
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class QiyuInboundCheckSum {
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String encode(String appSecret, String nonce, String time) {
String content = appSecret + nonce + time;
try {
MessageDigest messageDigest = MessageDigest.getInstance("sha1");
messageDigest.update(content.getBytes(StandardCharsets.UTF_8));
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(String appSecret, String nonce, String time, String checkSum) {
String expected = encode(appSecret, nonce, time);
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
valueOf(checkSum).getBytes(StandardCharsets.UTF_8));
}
private static String valueOf(String value) {
return value == null ? "" : value;
}
private static String getFormattedText(byte[] bytes) {
StringBuilder buf = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
buf.append(HEX_DIGITS[(b >> 4) & 0x0f]);
buf.append(HEX_DIGITS[b & 0x0f]);
}
return buf.toString();
}
}常见场景差异
| 场景 | 参数位置 | 签名说明 |
|---|---|---|
| 商品和订单查询、机器人服务先知、一触即达 | 通常在 Header 中携带 appid/appId、time、nonce、checksum/checkSum | 常见公式为 SHA1(appSecret + nonce + time)。 |
| CRM 用户认证接口 | Header 携带 time、checksum,Body 为认证参数 | 部分文档会把 nonce 定义为 MD5(body).toLowerCase(),此时按 SHA1(appSecret + nonce + time) 计算。 |
| 在线实时消息 URL 验证 | 通常在 Query 中携带 time、nonce、echostr、checksum | 该场景会把加密消息体或解密后的字符串纳入验证和回显,必须按对应页面说明实现。 |
| 事件推送、工单回调等业务事件 | Query 或 Header,具体以业务页为准 | 字段名、签名字段和失败响应结构可能随业务不同而不同。 |
如果具体业务页面给出了不同的参数位置、字段大小写或签名原文,应以具体业务页面为准。
通用错误码列表
| 错误码 | 含义 |
|---|---|
| 200 | OK |
| 14001 | 参数 appKey 错误 |
| 14002 | 参数 checksum 错误,请重新检查 checksum 计算方式 |
| 14003 | 参数 time 错误,请确保 time 参数与当前时间差小于 5 分钟,或检查服务的时间是否与标准时间同步 |
| 14004 | 内容格式校验错误,请参考详细错误信息,并对照对应的接口文档修正 |
| 14008 | 请求来源 IP 不在被允许的范围内 |
| 14009 | 请求频率过快,超出接口限制 |
| 14500 | 服务器内部错误,请反馈给我们具体场景 |
| 14501 | 数据量过大,超出接口限制 |
| 14515 | 权限不足 |
备注:如果 Java 应用调用七鱼服务端接口出现证书报错问题,请升级 JDK 版本至 1.8_202 及以上。
这篇文档有帮助吗?