API 接入文档
通过 API Key 将 apkgo cloud 集成到你的 CI/CD 流水线中
认证方式
Open API 的 base path 是 /openapi/v1,路径中不需要带 orgId — 组织信息已经绑定在 API Key 上。请求头加上 X-API-Key 即可。
curl -H "X-API-Key: apkgo_your_key_here" \ https://apkgo.baici.tech/openapi/v1/uploads
Open API 只接受 X-API-Key,不接受 JWT;Dashboard 仍然走 /api/v1/orgs/{orgId}/...。
上传 APK(三步直传)
APK 字节不再经过 apkgo cloud 服务器,而是直传对象存储。完整流程三步:领上传凭证 → 直传 APK → 创建分发任务。
第 1 步 · 获取上传凭证
curl -X POST \
-H "X-API-Key: apkgo_your_key_here" \
-H "Content-Type: application/json" \
-d '{"file_name": "app-release.apk"}' \
https://apkgo.baici.tech/openapi/v1/uploads/tickets
# → {"data": {"provider": "qiniu",
# "object_key": "apks/<org>/incoming/<uuid>-app-release.apk",
# "upload_url": "https://upload-xxx.qiniup.com",
# "token": "<uptoken>",
# "expires_at": "..."}}第 2 步 · 直传 APK 到对象存储
curl -X POST \ -F "token=<token>" \ -F "key=<object_key>" \ -F "file=@app-release.apk" \ <upload_url>
这一步直连七牛上传节点,带宽与 apkgo cloud 服务器无关。凭证 1 小时内有效,仅能写入该 object_key、限 1GB。
第 3 步 · 创建分发任务(JSON)
curl -X POST \
-H "X-API-Key: apkgo_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"object_key": "<object_key>",
"package_name": "com.example.app",
"version_name": "1.2.0",
"version_code": 12,
"release_notes": "修复若干问题",
"target_stores": ["huawei", "oppo", "vivo", "honor"]
}' \
https://apkgo.baici.tech/openapi/v1/uploads返回 202 Accepted,包含 job ID,上传在后台异步执行。package_name 必填(传 app_id 时可省):服务器不再解析 APK,worker 下载后会按二进制实际包名校验,不一致任务会失败。target_stores 省略时分发到该应用绑定的全部商店。可选 sha256 供 worker 校验包体完整性。
支持的商店:huawei、xiaomi、oppo、vivo、honor、tencent、samsung、googleplay、pgyer、fir。未在控制台为应用绑定凭证的商店会被自动跳过。
CI 脚本示例(bash + jq)
KEY="apkgo_your_key_here"
BASE="https://apkgo.baici.tech/openapi/v1"
APK="app-release.apk"
T=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"file_name\":\"$APK\"}" $BASE/uploads/tickets)
OBJ=$(echo "$T" | jq -r .data.object_key)
curl -sf -F "token=$(echo "$T" | jq -r .data.token)" \
-F "key=$OBJ" -F "file=@$APK" "$(echo "$T" | jq -r .data.upload_url)"
curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"object_key\":\"$OBJ\",\"package_name\":\"com.example.app\"}" \
$BASE/uploads⚠️ 旧版 -F "apk=@..." multipart 直传接口已移除,继续调用会收到 400 与迁移提示。
查询上传状态
curl -H "X-API-Key: apkgo_your_key_here" \
https://apkgo.baici.tech/openapi/v1/uploads/{jobId}状态值:pending → processing → completed 或 failed
API 端点
/openapi/v1/uploads/tickets获取直传对象存储的上传凭证/openapi/v1/uploads围绕已直传的 APK 创建分发任务/openapi/v1/uploads列出最近的上传任务/openapi/v1/uploads/{jobId}查询单个任务状态与结果/openapi/v1/uploads/{jobId}/cancel取消任务/openapi/v1/uploads/{jobId}/retry重试失败任务应用与凭证的管理(创建、绑定、修改)只在控制台进行;上传时应用会按 package_name 自动查找或创建,目标商店从你在控制台配好的绑定中解析。
权限
API Key 携带一组权限(默认 ["upload"])。当前 Open API 所有端点都需要 upload 权限;通配 "*" 等于全开。
错误处理
所有 API 响应格式统一:
// 成功
{"data": { ... }}
// 失败
{"error": "错误信息"}401API Key 缺失、无效或过期403权限不足(缺 upload 权限或超出套餐配额)429请求频率超限(600 次 / 分钟)Webhook 回调
在 API 密钥页面配置 Webhook URL 后,每次上传任务完成(成功或失败)系统会自动向该 URL 发送 POST 请求。
请求格式
POST https://your-server.com/webhook
Content-Type: application/json
X-Webhook-Signature: sha256=<HMAC-SHA256(body, secret)>
{
"event": "upload.completed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"app_name": "我的应用",
"package_name": "com.example.app",
"version_name": "1.2.0",
"version_code": 12,
"status": "completed",
"results": [
{
"store_name": "huawei",
"success": true,
"duration_ms": 3200
},
{
"store_name": "xiaomi",
"success": false,
"error": "credential expired",
"duration_ms": 1500
}
],
"timestamp": "2026-04-10T10:00:00Z"
}事件类型
upload.completed所有商店上传成功upload.failed至少一个商店上传失败签名验证
如果配置了签名密钥,请求头 X-Webhook-Signature 包含 HMAC-SHA256 签名。验证示例:
# Python
import hmac, hashlib
def verify(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return signature == f"sha256={expected}"
# Go
func verify(body []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expected))
}
# Node.js
const crypto = require("crypto");
function verify(body, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return signature === expected;
}接收端示例
# Python (Flask)
@app.route("/webhook", methods=["POST"])
def webhook():
sig = request.headers.get("X-Webhook-Signature", "")
if SECRET and not verify(request.data, sig, SECRET):
return "invalid signature", 401
data = request.json
if data["event"] == "upload.completed":
for r in data["results"]:
if r["success"]:
print(f"✅ {r['store_name']}: {r['duration_ms']}ms")
else:
print(f"❌ {r['store_name']}: {r['error']}")
return "ok"
# Go
func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
// verify signature...
var payload struct {
Event string `json:"event"`
AppName string `json:"app_name"`
Status string `json:"status"`
Results []struct {
StoreName string `json:"store_name"`
Success bool `json:"success"`
Error string `json:"error"`
DurationMs int64 `json:"duration_ms"`
} `json:"results"`
}
json.Unmarshal(body, &payload)
for _, r := range payload.Results {
if r.Success {
log.Printf("✅ %s: %dms", r.StoreName, r.DurationMs)
} else {
log.Printf("❌ %s: %s", r.StoreName, r.Error)
}
}
w.Write([]byte("ok"))
}注意事项
- • 请求超时 10 秒,超时不重试
- • 返回 2xx 视为成功,其他状态码记录日志但不重试
- • 签名密钥为空时不发送 X-Webhook-Signature 头
- • Webhook 在 API 密钥页面底部配置