洋葱头openAPI接口文档
更新日期:2026-04-15 20:35:54
版本: 1.0.1
联系: Oray Inc.
服务器: https://openapi.yctou.net/openapi
错误码说明
响应格式
所有接口的响应都遵循以下统一格式:
{
"code": 0,
"message": "success",
"data": {}
}
错误码分类
| 错误类型 | 说明 |
|---|---|
| 业务错误 | 参数校验、数据验证等业务逻辑错误 |
| 数据库错误 | 数据库操作失败 |
| 权限错误 | 认证失败、无权限访问等 |
| 系统错误 | 系统内部错误、服务不可用等 |
通用错误码
| 错误码常量 | 值 | 说明 | HTTP状态码 |
|---|---|---|---|
| CodeSysOk | 0 | 请求成功 | 200 |
| CodeSysFail | -1 | 系统操作错误 | 500 |
| CodeSysDataServerError | -3 | 数据服务异常 | 500 |
| CodeSysUserNoPermission | -4 | 无权限操作 | 403 |
| CodeSysInvalidParamsInput | -2 | 无效参数输入 | 400 |
| CodeUserUnauthorized | -17 | 用户未授权操作 | 401 |
OpenAPI 专用错误码
认证类错误码 (ModuleOpenAPIAuth)
| 错误码常量 | 值 | 说明 | HTTP状态码 |
|---|---|---|---|
| CodeOpenAPIMissingSignature | 1201001 | 缺少签名或时间戳 | 401 |
| CodeOpenAPIInvalidTimestamp | 1201002 | 无效时间戳格式 | 401 |
| CodeOpenAPITimestampExpired | 1201003 | 时间戳已过期 | 401 |
| CodeOpenAPIMissingApiKey | 1201004 | 缺少 API Key | 401 |
| CodeOpenAPIInvalidApiKey | 1201005 | 无效 API Key | 401 |
| CodeOpenAPIInvalidSignature | 1201006 | 无效签名 | 401 |
| CodeOpenAPIMissingToken | 1201007 | 缺少 Token | 401 |
| CodeOpenAPIInvalidToken | 1201008 | 无效 Token | 401 |
| CodeOpenAPIInvalidScope | 1201009 | 无效 scope | 403 |
| CodeOpenAPIMissingTeamId | 1201010 | Token 缺少 team id | 401 |
| CodeOpenAPITeamIdEmpty | 1201011 | teamId 为空 | 403 |
Vault 业务类错误码 (ModuleOpenAPIVault)
| 错误码常量 | 值 | 说明 | HTTP状态码 |
|---|---|---|---|
| CodeOpenAPIVaultAccountEmpty | 1202001 | 账号不能为空 | 400 |
| CodeOpenAPIVaultAliasEmpty | 1202002 | 别名不能为空 | 400 |
| CodeOpenAPIVaultAppIdsEmpty | 1202003 | 应用 ID 列表为空 | 400 |
| CodeOpenAPIVaultAppIdsInvalid | 1202004 | 应用 ID 不合法 | 400 |
| CodeOpenAPIVaultTeamIdInvalid | 1202005 | teamId 无效 | 400 |
| CodeOpenAPIVaultUnauthorizedApp | 1202006 | 无权访问指定应用 | 403 |
| CodeOpenAPIVaultUnauthorizedMember | 1202007 | 无权访问指定成员 | 403 |
| CodeOpenAPIVaultUnauthorizedGroup | 1202008 | 无权访问指定分组 | 403 |
认证方式
API Key 认证
用于获取 Token 接口 (/token)。
请求头参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-API-Key | string | 是 | API Key ID |
| X-Signature | string | 是 | HMAC-SHA256 签名 |
| X-Timestamp | string | 是 | Unix 时间戳(秒级) |
签名算法:
1.将所有参数按字母顺序排序
2.拼接成 k1=v1&k2=v2 格式
3.使用 HMAC-SHA256 计算签名
签名算法详解
签名生成步骤
1. 准备参数
- api_key: 你的 API Key ID
- timestamp: 当前 Unix 时间戳(秒级),如 "1711234567"
- secret: 你的 API Key Secret(用于生成签名的密钥)
2. 构造待签名参数表
按字母顺序排序所有参数:
- timestamp = "1711234567"
- api_key = "your_api_key_id"
排序后顺序不变(字母顺序:api_key < timestamp)
3. 拼接签名字符串
按 "key=value" 格式拼接,用 "&" 连接:
sign_string = "api_key=your_api_key_id×tamp=1711234567"
4. 计算 HMAC-SHA256 签名
使用 secret 作为密钥,对 sign_string 进行 HMAC-SHA256 签名:
signature = HMAC-SHA256(secret, sign_string)
然后将签名结果转换为十六进制字符串
Golang 签名示例
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
)
// GenerateSignature 生成签名
// secret: API Key Secret
// params: 所有参与签名的参数
func GenerateSignature(secret string, params map[string]string) string {
// 1. 按字母顺序排序参数
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 2. 拼接签名字符串
var signStr string
for i, k := range keys {
if i > 0 {
signStr += "&"
}
signStr += k + "=" + params[k]
}
// 3. 计算 HMAC-SHA256
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(signStr))
signature := hex.EncodeToString(mac.Sum(nil))
return signature
}
func main() {
// 示例参数
secret := "your_api_key_secret"
params := map[string]string{
"api_key": "your_api_key_id",
"timestamp": "1711234567",
}
// 生成签名
signature := GenerateSignature(secret, params)
fmt.Println("Signature:", signature)
}
其他语言签名示例
JavaScript/Node.js:
const crypto = require('crypto');
function generateSignature(secret, params) {
// 1. 按字母顺序排序参数
const keys = Object.keys(params).sort();
// 2. 拼接签名字符串
const signStr = keys.map(k => `${k}=${params[k]}`).join('&');
// 3. 计算 HMAC-SHA256
const hmac = crypto.createHmac('sha256', secret);
hmac.update(signStr);
return hmac.digest('hex');
}
// 示例
const secret = 'your_api_key_secret';
const params = {
api_key: 'your_api_key_id',
timestamp: '1711234567'
};
const signature = generateSignature(secret, params);
console.log('Signature:', signature);
Python:
import hmac
import hashlib
import sort
def generate_signature(secret, params):
# 1. 按字母顺序排序参数
sorted_keys = sorted(params.keys())
# 2. 拼接签名字符串
sign_str = '&'.join(f"{k}={params[k]}" for k in sorted_keys)
# 3. 计算 HMAC-SHA256
signature = hmac.new(
secret.encode('utf-8'),
sign_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
# 示例
secret = 'your_api_key_secret'
params = {
'api_key': 'your_api_key_id',
'timestamp': '1711234567'
}
signature = generate_signature(secret, params)
print(f'Signature: {signature}')
签名验证注意事项
| 注意事项 | 说明 |
|---|---|
| 时间戳容差 | 时间戳有效期为 5 分钟(300秒),超出将返回 1201003 错误 |
| 字母排序 | 参数排序使用英文字母顺序(ASCII),区分大小写 |
| 参数编码 | 参数值保持原始字符串,不需要 URL 编码 |
| timestamp 必须参与签名 | security_timestamp 参数必须包含在签名参数中 |
| 空值处理 | 空字符串参数也参与签名,但可以为空字符串 |
常见签名错误
| 错误码 | 错误内容 | 原因 |
|---|---|---|
| 1201001 | 缺少签名或时间戳 | 未提供 signature 或 timestamp 参数 |
| 1201002 | 无效时间戳格式 | timestamp 不是有效的 Unix 时间戳 |
| 1201003 | 时间戳已过期 | timestamp 超出 5 分钟容差 |
| 1201006 | 无效签名 | 签名计算结果不匹配 |
Bearer Token 认证
用于业务接口 (/v1/*)。
请求头:Authorization: Bearer <token>
接口列表
Token
POST /token - 生成 Token
使用 API Key 获取访问 Token。
认证方式: API Key + 签名验证
HEADER
| 参数 | 说明 |
|---|---|
| X-Signature | 签名 |
| X-Timestamp | Unix 时间戳(秒级) |
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| api_key | string | 是 | API Key ID |
成功响应 (200):
{
"code": 0,
"message": "success",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": 1711234567
}
}
错误响应 (401):
| 错误码 | 错误内容 | 错误原因 |
|---|---|---|
| 1201001 | 缺少签名或时间戳 | 缺少 X-Signature 或 X-Timestamp 请求头 |
| 1201002 | 无效时间戳格式 | 时间戳格式不正确,无法解析 |
| 1201003 | 时间戳已过期 | 时间戳超过 5 分钟容差 |
| 1201004 | 缺少 API Key | 缺少 api_key 参数 |
| 1201005 | 无效 API Key | API Key 不存在或已禁用 |
| 1201006 | 无效签名 | 签名验证失败 |
Vault
POST /v1/vault - 创建账号密码
创建一个新的账号密码记录。
认证方式: Bearer Token
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| account | string | 是 | 账号,最大长度 50 |
| account_id | string | 否 | 账号ID |
| password | string | 否 | 密码,最大长度 50 |
| alias | string | 是 | 别名,最大长度 50 |
| team_application_ids | array[string] | 是 | 团队应用ID数组 |
| member_ids | array[string] | 否 | 成员ID数组(越权检查) |
| group_ids | array[string] | 否 | 分组ID数组(越权检查) |
| group_status_id | integer | 否 | 分组状态ID(0=未授权, 1=指定分组/成员, 2=授权全部成员) |
| category_ids | array[string] | 否 | 账号分类ID数组(越权检查,只允许 type_id=3 的分类) |
权限说明:
●team_id 从 Token 中自动提取
●team_application_ids、member_ids、group_ids 会进行越权检查
●只有属于当前团队的合法 ID 才会被写入
成功响应 (200):
{
"code": 0,
"message": "success",
"data": {
"key_id": "1234567890"
}
}
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | 1202005 | teamId 无效 | teamId 为 0 |
| 400 | 1202001 | 账号不能为空 | account 字段为空 |
| 400 | 1202002 | 别名不能为空 | alias 字段为空 |
| 400 | 1202003 | 应用 ID 列表为空 | team_application_ids 数组为空 |
| 400 | 1202004 | 应用 ID 不合法 | 应用 ID 不属于当前团队 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 401 | 1201010 | Token 缺少 team id | Token 中不包含 team id |
| 403 | 1201009 | 无效 scope | scope 不为 "openapi" |
| 403 | 1201011 | teamId 为空 | Token 中 team id 为 0 |
| 500 | -3 | 数据服务异常 | 数据库操作失败 |
GET /v1/vault - 获取账号密码列表
获取当前团队下的账号密码列表(全局列表,不按应用过滤)。
认证方式: Bearer Token
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | integer | 是 | 页码,最小值 1 |
| size | integer | 是 | 每页数量,最小值 1,最大值 100 |
| keyword | string | 否 | 关键词搜索 |
| keyword_type | integer | 否 | 搜索类型(0=别名, 1=账号) |
| type_id | integer | 否 | 账号类型(0=账号密码, 1=SMS) |
成功响应 (200):
{
"code": 0,
"message": "success",
"data": {
"total": 100,
"hits": [
{
"key_id": "1234567890",
"account": "test@example.com",
"account_id": "",
"alias": "测试账号",
"description": "测试描述",
"status_id": 1,
"type_id": 0,
"is_empty_pwd": 0,
"update_time": 1700000000,
"apps": [
{
"team_application_id": "1",
"name": "应用名称",
"url": "https://app.com",
"group_status_id": 1,
"status_id": 1
}
]
}
]
}
}
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | -2 | 无效参数输入 | 参数格式错误 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 500 | -3 | 数据服务异常 | 数据库操作失败 |
POST /v1/vault/grant - 账号密码-应用授权
为指定的账号密码绑定应用进行成员或分组授权。
认证方式: Bearer Token
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key_id | string | 是 | 账号ID |
| team_application_id | string | 是 | 团队应用ID |
| group_status_id | integer | 是 | 分组状态ID(0=未授权, 1=指定分组/成员, 2=授权全部成员) |
| member_ids | array[string] | 否 | 成员ID数组(当 group_status_id=1 时有效) |
| group_ids | array[string] | 否 | 分组ID数组(当 group_status_id=1 时有效) |
权限说明:
●team_id 从 Token 中自动提取
●key_id 必须属于当前团队(越权检查)
●team_application_id 必须属于当前团队(越权检查)
●member_ids、group_ids 会被过滤,只保留属于当前团队的合法 ID
成功响应 (200):
{
"code": 0,
"message": "success",
"data": null
}
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | -2 | 无效参数输入 | 参数格式错误或 group_status_id 不在 0-2 范围内 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 404 | -9 | 记录不存在 | key_id 或 team_application_id 不属于当前团队 |
| 500 | -3 | 数据服务异常 | 数据库操作失败 |
GET /v1/vault/categories - 获取账号分类列表
获取当前团队下的所有分类。
认证方式: Bearer Token
成功响应 (200):
{
"code": 0,
"message": "success",
"data": {
"node_list": [
{
"id": 1,
"name": "分类1"
},
{
"id": 2,
"name": "分类2"
}
]
}
}
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | 1202005 | teamId 无效 | teamId 为 0 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 403 | 1201009 | 无效 scope | scope 不为 "openapi" |
| 403 | 1201011 | teamId 为空 | Token 中 team id 为 0 |
| 500 | -3 | 数据服务异常 | 数据库查询失败 |
TeamApplication
GET /v1/team-applications/options - 获取团队应用列表
获取当前团队下的所有应用及其分类信息。
认证方式: Bearer Token
成功响应 (200):
{
"code": 0,
"data": {
"apps": [
{
"team_application_id": 1,
"name": "应用名称",
"category_ids": [1, 2]
}
],
"categories": [
{
"id": 1,
"name": "分类名称"
}
]
}
}
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | 1202005 | teamId 无效 | teamId 为 0 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 403 | 1201009 | 无效 scope | scope 不为 "openapi" |
| 403 | 1201011 | teamId 为空 | Token 中 team id 为 0 |
| 500 | -3 | 数据服务异常 | 数据库查询失败 |
Organization
GET /v1/org/departments - 获取组织架构
获取部门/分组和成员的树形结构。
认证方式: Bearer Token
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | integer | 否 | 组织类型筛选:0-全部,1-仅部门/分组,2-仅成员 |
成功响应 (200):
{
"code": 0,
"data": {
"hits": [
{
"id": "1",
"dept_id": "1",
"name": "部门名称",
"is_group": true,
"is_grant": true,
"member_count": 10,
"total_member_count": 100,
"children": []
}
]
}
}
OrgNode 节点属性:
| 属性 | 类型 | 说明 |
|---|---|---|
| id | string | 节点ID |
| dept_id | string | 部门ID(仅部门节点有值) |
| name | string | 节点名称 |
| is_group | boolean | 是否为分组/部门(true为分组,false为成员) |
| is_grant | boolean | 是否有权限 |
| member_count | integer | 成员数量(仅分组节点有值) |
| total_member_count | integer | 总成员数量(仅分组节点有值) |
| children | array | 子节点 |
| member_type | integer | 成员类型(仅成员节点有值) |
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | 1202005 | teamId 无效 | teamId 为 0 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 403 | 1201009 | 无效 scope | scope 不为 "openapi" |
| 403 | 1201011 | teamId 为空 | Token 中 team id 为 0 |
| 500 | -3 | 数据服务异常 | 数据库查询失败 |
SMS
GET /v1/sms/list - 获取短信列表
获取当前团队的短信发送记录。
认证方式: Bearer Token
请求参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | integer | 否 | 1 | 页码 |
| page_size | integer | 否 | 20 | 每页数量 |
成功响应 (200):
{
"code": 0,
"data": {
"hits": [],
"total": 0
}
}
错误响应:
| HTTP码 | 错误码 | 错误内容 | 错误原因 |
|---|---|---|---|
| 400 | 1202005 | teamId 无效 | teamId 为 0 |
| 401 | 1201007 | 缺少 Token | 缺少 Authorization 请求头 |
| 401 | 1201008 | 无效 Token | Token 格式错误或已过期 |
| 403 | 1201009 | 无效 scope | scope 不为 "openapi" |
| 403 | 1201011 | teamId 为空 | Token 中 team id 为 0 |
| 500 | -3 | 数据服务异常 | 数据库查询失败 |
数据结构
ErrorResponse
错误响应结构。
| 属性 | 类型 | 说明 |
|---|---|---|
| code | integer | 错误码 |
| message | string | 错误信息 |
| data | object | 错误详情(可选) |
文档内容是否对您有帮助?
如果遇到产品相关问题,您可咨询 在线客服 寻求帮助。



相关问题
其他问题