- URL:
/api/v1/user/getEmailRegisterValidCode - Method:
POST - Content-Type:
application/json - Request Body:
{ "email": "user@example.com", "channel": "gmk" } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": "c1f8e4d7-c8a0-4b3e-8b1e-5e6f4d8a3b1c" }
- Code:
- Error Response:
- Code:
400(邮箱格式无效) - Code:
500(发送或存储验证码失败)
- Code:
- URL:
/api/v1/user/doLoginByEmail - Method:
POST - Content-Type:
application/json - Request Body:
{ "email": "user@example.com", "validCode": "123456", "validCodeReqNo": "c1f8e4d7-c8a0-4b3e-8b1e-5e6f4d8a3b1c", "app": "aipc" } - 说明:
- 可选字段
app:客户端 App 标识,用于为用户打上tb_users.app_aipc/app_herdsman/app_flowymes标记,并记录对应app_*_at首次登录时间,便于运营统计。取值:aipc、herdsman、flowymes(大小写不敏感);未传或未知值时不更新标记。
- 可选字段
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Login successful", "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
- Code:
- Error Response:
- Code:
400(请求无效或验证码错误) - Code:
500(登录失败)
- Code:
- URL:
/api/v1/user/bindEmail - Method:
POST - Headers:
Authorization: Bearer <C端JWT>
- 说明:
- 该接口用于第三方登录(如微信)后绑定邮箱。
- 验证码通过
/api/v1/user/getBindEmailValidCode发送(与邮箱登录验证码为两套独立校验链路)。 - 成功绑定后,邮箱经规范化(trim、小写)写入;在当前用户所在 channel 内不可与其他用户重复,不同 channel 下允许相同邮箱。
- 若同 channel 下该邮箱已被其他用户占用:仅当占用方为纯邮箱账户(无
open_id/union_id)且无已支付订单(paid_at非空)时,服务端会删除该纯邮箱账户并完成绑定;若占用方已绑定微信/第三方,或存在已支付订单,则拒绝绑定(分别返回error.user.email_already_in_use/error.user.email_taken_with_benefits)。
- Request Body:
{ "email": "user@example.com", "validCode": "123456", "validCodeReqNo": "c1f8e4d7-c8a0-4b3e-8b1e-5e6f4d8a3b1c" } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
- Code:
- Error Response:
- Code:
400(error.auth.invalid_or_expired_verification_code:验证码无效或已过期) - Code:
400(error.user.email_already_in_use:邮箱已被使用) - Code:
401(error.unauthorized:未登录/Token 无效)
- Code:
- URL:
/api/v1/user/getBindEmailValidCode - Method:
POST - Headers:
Authorization: Bearer <C端JWT>
- Request Body:
{ "email": "user@example.com" } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": "c1f8e4d7-c8a0-4b3e-8b1e-5e6f4d8a3b1c" }
- Code:
- Error Response:
- Code:
400(error.user.email_already_in_use:同 channel 下该邮箱已被其他用户使用,见 2.1 说明) - Code:
401(error.unauthorized:未登录/Token 无效) - Code:
500(error.send_verification_code_failed:发送验证码失败)
- Code:
- URL:
/api/v1/auth/third/callback - Method:
GET - Query Parameters:
platform:WECHAT(目前仅支持微信)code: 从第三方获取的授权码app(可选): 客户端 App 标识,取值aipc/herdsman/flowymes,登录成功后写入用户表对应标记字段
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Login successful", "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
- Code:
- Error Response:
- Code:
400(缺少code或不支持的platform) - Code:
500(微信登录失败)
- Code:
与「网页 OAuth 微信登录」共用同一服务号 app_id / app_secret,用户 openid 与 /api/v1/auth/third/callback?platform=WECHAT 一致。需在配置中为对应 channel 填写 wechat.<channel>.mp_server_token(与公众平台「服务器配置」Token 一致),否则创建会话接口返回 503(error.wechat_mp_login_disabled)。若公众平台启用 安全模式 或 兼容模式,还需配置 wechat.<channel>.mp_encoding_aes_key(与公众平台 EncodingAESKey 一致,43 字符);解密后的尾缀 appid 须与当前 channel 的 app_id 一致,故多 channel 时请仅在对应服务号的 channel 上填写该密钥。
流程:客户端调用 POST /session 获取 sessionId 与二维码 URL → 用户微信扫码并关注服务号(已关注用户扫码会触发 SCAN)→ 客户端轮询 GET /session/status 直至 status=confirmed 取得 JWT。
- URL:
/api/v1/auth/wechat-mp/session - Method:
POST - Content-Type:
application/json - Request Body:
{ "channel": "flowy", "inviteCode": "" } - 字段说明:
channel: 与邮箱登录等业务一致(可选,默认flowy)inviteCode: 可选;若填写,登录成功后服务端会尝试绑定邀请码(失败不阻断登录,与第三方 OAuth 回调行为一致)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "sessionId": "32位十六进制字符串与二维码场景值一致", "qrImageUrl": "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=...", "expiresIn": 300 } }
- Code:
- Error Response:
- Code:
503(error.wechat_mp_login_disabled:未配置mp_server_token) - Code:
500(error.wechat_mp_qrcode_failed:创建二维码失败)
- Code:
- URL:
/api/v1/auth/wechat-mp/session/status - Method:
GET - Query Parameters:
sessionId: 创建会话返回的sessionId(必填)
- Success Response:
- Code:
200 - Body(等待扫码/关注):
{ "code": 200, "msg": "操作成功", "data": { "status": "pending" } } - Body(登录成功):
{ "code": 200, "msg": "操作成功", "data": { "status": "confirmed", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } } - Body(会话已过期或不存在):
{ "code": 200, "msg": "操作成功", "data": { "status": "expired" } }
- Code:
- Error Response:
- Code:
400(error.wechat_mp_session_id_required:缺少sessionId) - Code:
500(error.internal)
- Code:
- URL:
/api/v1/auth/wechat-mp/callback - Method:
GET(URL 校验)、POST(事件推送) - 说明:
- 在微信公众平台「开发 — 基本配置 — 服务器配置」中将 服务器 URL 填为:
https://<你的域名>/api/v1/auth/wechat-mp/callback(需与对外暴露的 API 前缀一致)。 - Token 与配置项
wechat.<channel>.mp_server_token一致(同一主体下多 channel 若共用同一公众号,各 channel 应配置相同 Token)。 - 安全 / 兼容模式(请求带
msg_signature,且GET的echostr或POST体含<Encrypt>):使用sha1(sort(token, timestamp, nonce, encrypt))与msg_signature比对;再用mp_encoding_aes_key做 AES-256-CBC 解密(与公众平台算法一致),并校验明文包尾appid与wechat.<channel>.app_id一致。 - 明文模式(未使用上述加密参数时):
GET使用signature与sha1(sort(token, timestamp, nonce))验签,通过后原样返回echostr;POST直接解析明文 XML(不校验msg_signature,兼容旧部署)。 GET(安全模式):解密echostr得到明文字符串,将该字符串作为 HTTP 响应体返回(非密文回传)。POST(安全模式):解密<Encrypt>内密文得到内层 XML,再处理MsgType=event且Event=subscribe或SCAN;业务处理完成后 HTTP 200 正文返回明文success。
- 在微信公众平台「开发 — 基本配置 — 服务器配置」中将 服务器 URL 填为:
- URL:
/api/v1/user/me - Method:
GET - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "id": 1, "created_at": "2023-10-27T10:00:00+08:00", "updated_at": "2023-10-27T10:00:00+08:00", "deleted_at": null, "username": "test_user", "password": "", "nickname": "Test User", "avatar": "http://example.com/avatar.jpg", "email": "test@example.com", "phone": "13800138000", "country": "CN", "city": "Beijing", "language": "zh-CN", "open_id": "wx_openid_123", "union_id": "wx_unionid_123", "status": 1, "last_login_ip": "127.0.0.1", "last_login_time": "2023-10-27T10:00:00+08:00", "currentPlan": { "planId": 2, "code": "ProMonth", "name": "专业版(月)", "nameEn": "Pro (Monthly)", "planPeriod": "MONTH", "currency": "CNY", "startAt": "2023-10-27T10:00:00+08:00", "endAt": "2023-11-26T10:00:00+08:00", "status": "ACTIVE", "source": "personal" }, "currentWranglerMembership": { "membershipId": 20, "planId": 8, "code": "wrangler_gold", "name": "牧马人金卡", "nameEn": "Wrangler Gold", "planPeriod": "MONTH", "currency": "CNY", "startAt": "2026-03-25T10:00:00+08:00", "endAt": "2026-04-24T10:00:00+08:00", "status": "ACTIVE", "source": "purchase" }, "team": { "teamId": 10, "name": "My Team", "role": "MEMBER", "ownerUserId": 1, "ownerUser": { "userId": 1, "nickname": "Owner", "avatar": "http://example.com/avatar.jpg" }, "plan": { "planId": 3, "code": "TeamPro", "name": "团队专业版", "nameEn": "Team Pro", "currency": "CNY", "startAt": "2023-10-27T10:00:00+08:00", "endAt": "2023-11-26T10:00:00+08:00", "status": "ACTIVE" } } } }
- Code:
- Error Response:
- Code:
401(Unauthorized)
- Code:
- URL:
/api/v1/user/clientPackage - Method:
POST - Headers:
Authorization: Bearer <token>
- Request Body:
{ "packageType": "alpha", "appVersion": "1.2.3", "platform": "ios", "clientId": "device-uuid-xxx" } - 字段说明:
packageType: 版本类型(必填,例如 alpha/stable)appVersion: 版本号(可选,建议上报以便统计“版本类型+版本号”分布)platform: 平台(可选,例如 ios/android/web/desktop)clientId: 客户端实例 ID(可选,用于区分同一用户多端)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success" }
- Code:
- Error Response:
- Code:
400(参数非法) - Code:
401(Unauthorized)
- Code:
- URL:
/api/v1/ota/packages - Method:
GET - Query Params:
page: 页码(默认1)pageSize: 每页条数(默认10,最大200)app: 应用标识(可选,默认flowyclaw)platform: 平台(可选,例如windows/macos/linux)arch: 架构(可选,例如x64/arm64)channel: 包渠道(可选,例如stable/beta)partnerChannel: 第三方渠道(可选;预装客户,例如GMK/Lenovo;留空表示通用包)
- 说明:
- 仅返回至少包含一条更新说明的记录(
bugFixes/newFeatures/improvements任一分类的中英文条目,或兼容字段release_notes_md);过滤逻辑与响应序列化一致(会排除[]、[""]等解析后为空的内容);total与分页均基于过滤后的结果集。
- 仅返回至少包含一条更新说明的记录(
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "list": [ { "id": 1, "app": "flowyclaw", "platform": "windows", "arch": "x64", "channel": "stable", "partnerChannel": "LENOVO", "version": "1.2.3", "buildNumber": 0, "bugFixes": ["修复启动崩溃问题"], "bugFixesEn": ["Fix startup crash"], "newFeatures": ["新增离线安装包校验"], "newFeaturesEn": ["Add offline package verification"], "improvements": ["优化下载速度与断点续传"], "improvementsEn": ["Improve download speed and resume"], "isForce": 0, "minVersion": "1.0.0", "fileName": "installer.exe", "fileSize": 12345678, "sha256": "xxx", "modelscopeModelId": "owner/repo", "modelscopeRevision": "master", "modelscopeFilePath": "ota/...", "downloadUrl": "https://...", "status": 1, "sort": 0, "createdAt": "2026-03-12T08:00:00Z", "updatedAt": "2026-03-12T08:00:00Z" } ], "total": 1 } }
- Code:
- Error Response:
- Code:
500(查询失败)
- Code:
- URL:
/api/v1/ota/packages/by-version - Method:
GET - Query Params:
version: 目标版本号(必填,语义化版本,例如0.2.10-alpha.20260330.0)app: 应用标识(可选)platform: 平台(可选,例如windows/macos/linux)arch: 架构(可选,例如x86/x64/arm64)channel: 包渠道(可选,例如alpha/stable)partnerChannel: 第三方渠道(可选;预装客户,例如GMK/Lenovo)buildNumber: 构建号(可选;与库表唯一键一致时用于精确匹配)
- 说明:
- 返回指定版本对应的 OTA 记录(含
status与更新日志字段),不限于已发布状态,便于客户端展示更新说明并根据status判断是否可升级。 - 当存在多条匹配记录时,返回
id最大的一条。
- 返回指定版本对应的 OTA 记录(含
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 1, "app": "flowyclaw", "platform": "windows", "arch": "x86", "channel": "alpha", "partnerChannel": "FLOWY", "version": "0.2.10-alpha.20260330.0", "buildNumber": 0, "bugFixes": ["修复启动崩溃问题"], "bugFixesEn": ["Fix startup crash"], "newFeatures": ["新增离线安装包校验"], "newFeaturesEn": ["Add offline package verification"], "improvements": ["优化下载速度与断点续传"], "improvementsEn": ["Improve download speed and resume"], "isForce": 0, "minVersion": "1.0.0", "fileName": "FlowyAIPC-cn-0.2.10-alpha.20260330.0-win-x64.exe", "fileSize": 516004208, "sha256": "", "modelscopeModelId": "", "modelscopeRevision": "", "modelscopeFilePath": "", "downloadUrl": "https://...", "status": 1, "sort": 0, "createdAt": "2026-03-12T08:00:00Z", "updatedAt": "2026-03-12T08:00:00Z" } } status取值:0草稿(未发布)/1已发布 /2已下架
- Code:
- Error Response:
- Code:
400(version或buildNumber参数非法) - Code:
404(未找到对应版本) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/hardware/products - Method:
GET - Query Params:
currency: 币种(可选,默认CNY;例如CNY/USD)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 1, "name": "FlowyClaw 硬件套件", "nameEn": "FlowyClaw Kit", "description": "硬件商品介绍", "descriptionEn": "Hardware product description", "currency": "CNY", "originalPriceCent": 199900, "priceCent": 159900, "linkUrl": "https://example.com/product", "imageUrl": "https://cdn.example.com/product.jpg", "sort": 0 } ] }
- Code:
- Error Response:
- Code:
500(查询失败)
- Code:
- URL:
/api/v1/otel/reportAddrs - Method:
GET - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "otelHttpAddr": "http://39.97.165.115:4318", "otelGrpcAddr": "http://39.97.165.115:4317" } }
- Code:
- Error Response:
- Code:
500(配置未加载或服务异常)
- Code:
- URL:
/api/v1/model/availableListClaw - Method:
GET - Query Parameters:
category(可选):tb_model.category,默认1(对话)。视频模型请传4(与constants.ModelCategoryVideo一致)。
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "Cloud": [ { "model_name": "qwen-long", "model_provider": "aliyun", "model_type": "text-generation" } ] } }
- Code:
- Error Response:
- Code:
500(获取模型列表失败)
- Code:
- 创建任务
- URL:
/api/v1/video/generations/tasks - Method:
POST - Headers:
Authorization: Bearer <token> - Body: 与方舟「创建视频生成任务」一致(JSON)。服务端将
model解析为tb_channel_model中model_category=4的渠道模型(客户端model建议与可用列表一致,形如flowy/<tb_model.name>),使用渠道上的api_key与方舟model调用上游;callback_url若配置了seedance.callback_url则由服务端覆盖写入。 - 成功:
data仅包含{ "id": <tb_video_task 自增主键> },无其他字段。
- URL:
- 任务列表(仅读库、分页)
- URL:
/api/v1/video/generations/tasks - Method:
GET - Headers:
Authorization: Bearer <token> - Query:
page(默认 1)、pageSize(默认 10,最大 200) - 说明: 返回当前登录用户的全部视频任务摘要,按本地主键
id倒序。data.list元素与单条查询字段一致(id、task_id、status、result、created_at、updated_at);data.total为总条数。
- URL:
- 查询任务(仅读库)
- URL:
/api/v1/video/generations/tasks/:id - Method:
GET - Headers:
Authorization: Bearer <token> - 说明: 路径
id为本地tb_video_task.id(自增)。data.task_id为方舟侧任务 id;data.result为保存在本地的上游任务 JSON 快照(不含内部_flowy元数据);data.status为本地状态码(1 queued / 2 running / 3 cancelled / 4 succeeded / 5 failed / 6 expired)。
- URL:
- 取消或删除任务
- URL:
/api/v1/video/generations/tasks/:id - Method:
DELETE - Headers:
Authorization: Bearer <token> - 说明: 路径
id为本地tb_video_task.id。服务端根据记录调用方舟删除接口并将本地状态置为已取消(3);若任务已取消则幂等成功。 - 常见错误: HTTP
409、code409:任务仍在生成等上游冲突(error.video_task.delete_running或error.video_task.delete_conflict);其他上游失败多为502、error.seedance_upstream_failed。
- URL:
- 方舟回调(无需登录)
- URL:
/api/v1/video/generations/callback - Method:
POST - 说明: 与配置中
seedance.callback_url路径一致。成功接收返回 HTTP200(无业务 JSON 包装要求,方舟以状态码为准)。
- URL:
- URL:
/api/v1/model/localUsage/report - Method:
POST - Headers:
Authorization: Bearer <token>
- 说明:
- C 端在本地模型推理完成后上报 Token 用量;服务端按配置项
model.local_model_billing_channel_model(tb_channel_model.model)匹配渠道模型,取其 input/output 单价计算等价云端积分(公式与/v1/chat/completions一致),供客户端展示「已为您节省 xxx 积分」等文案。 - 不扣减用户积分余额;记录写入
tb_user_local_model_call供运营统计。 - 同一用户下
idempotencyKey重复上报返回已存在记录(duplicate=true),不重复落库。
- C 端在本地模型推理完成后上报 Token 用量;服务端按配置项
- Request Body:
modelId(可选):客户端本地模型标识,未传或空字符串时仍会计价并落库,localModelId响应为空idempotencyKey(必填):同一用户下幂等键,最长 128 字符
{ "modelId": "Qwen3.5_4b", "promptTokens": 1200, "completionTokens": 400, "cacheTokens": 0, "sessionId": "sess-abc", "clientId": "PC", "idempotencyKey": "call-uuid-001", "extra": {} } - Success Response (
data):id: 上报记录 IDlocalModelId/billingChannelModelId: 本地模型标识与计价基准渠道模型 IDsavedPoints: 按基准模型价格计算的等价云端积分(展示用「节省额度」,不从余额扣除)duplicate: 是否为幂等重复请求
- 常见错误:
503/error.local_model_billing_not_configured:未配置local_model_billing_channel_model503/error.local_model_billing_model_not_found:基准模型不存在或非 active
- URL:
/api/v1/model/calls - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
page: 页码(从 1 开始,默认 1)pageSize: 每页条数(默认 10,最大 200)
- 说明:
- 返回当前登录用户的模型调用记录(按
chat_id倒序) credit_consumed为本次调用消耗积分(从积分流水记录中计算;若无对应流水则为 0)created_at为 RFC3339 时间字符串(时区取决于服务端时间配置)
- 返回当前登录用户的模型调用记录(按
- 字段说明:
chat_id: 用户对话 IDmodel_name: 模型名称(tb_models.name)channel_model_id: 渠道模型 ID(tb_channel_models.id)prompt_tokens: 输入 tokenscompletion_tokens: 输出 tokenscache_tokens: 缓存 tokens(如适用)credit_consumed: 积分消耗created_at: 调用记录创建时间
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "list": [ { "chat_id": 123, "model_name": "qwen-long", "channel_model_id": 10, "prompt_tokens": 100, "completion_tokens": 200, "cache_tokens": 50, "credit_consumed": 300, "created_at": "2026-03-09T10:00:00Z" } ], "total": 1 } }
- Code:
- Error Response:
- Code:
401(Unauthorized) - Body:
{ "error": "Invalid or expired token" } - Code:
500(获取调用记录失败)
- Code:
以下接口需 Authorization: Bearer <token>(C 端用户 JWT)。用于客户端在调用阿里云智能语音(NLS)实时识别等能力:可先拉取 AppKey(中英各一),再按需获取经服务端 RSA 公钥加密后的 Token;明文 Token 仅在服务端内存中处理,不直接下发。
- URL:
/api/v1/asr/aliyunAsrToken - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 服务端调用阿里云 NLS
CreateToken取得短期访问 Token,再使用配置的 RSA 公钥对明文 Token 做RSA/ECB/PKCS1Padding加密,将 Base64 编码的密文置于响应data.token。 - 客户端需使用与公钥配对的 私钥解密后,再传入阿里云语音 SDK(解密算法需与加密侧一致:
RSA/ECB/PKCS1Padding)。 expireTime与阿里云返回的 Token 过期时间一致(数值含义以阿里云接口为准,一般为 Unix 时间戳)。
- 服务端调用阿里云 NLS
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "token": "Base64EncodedCipherText...", "expireTime": 1710003600 } }
- Code:
- Error Response(节选):
- Code:
401(未登录或 token 无效) - Code:
503(error.asr_not_configured:服务端未配置阿里云 AK/SK 或未配置 RSA 公钥) - Code:
502(error.asr_aliyun_token_failed:调用阿里云获取 Token 失败) - Code:
500(error.config_not_loaded:配置未加载;或error.asr_token_encrypt_failed:RSA 加密失败)
- Code:
- URL:
/api/v1/asr/aliyunAsrKey - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 返回服务端配置的 NLS 应用 AppKey:
appKeyEn为英文场景,appKeyZh为中文场景(具体语义以阿里云控制台创建的项目为准)。 - 未配置时对应字段为空字符串。
- 返回服务端配置的 NLS 应用 AppKey:
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "appKeyEn": "yourAppKeyEn", "appKeyZh": "yourAppKeyZh" } }
- Code:
- Error Response(节选):
- Code:
401(未登录或 token 无效) - Code:
500(error.config_not_loaded:配置未加载)
- Code:
以下 /v1/* 模型相关接口(如 /v1/models、/v1/chat/completions、图生等)的 Authorization: Bearer 可为 C 端用户 JWT(通常以 eyJ 开头)或 用户 API 密钥(flowy- 前缀,在「用户 API 密钥」管理接口中创建)。服务端对 flowy- 会解析为对应 user_id 后走相同计费与限流;密钥解析结果会在 Redis 中按配置 user_api_key.cache_ttl 缓存。
另:/api/v1/ocr/...、视频生成任务 /api/v1/video/generations/... 与上述规则相同(JWT 或 flowy-)。管理类接口(如 /api/v1/user/apiKeys、账户信息、订单等)仍 仅支持 JWT。
流式对话:/v1/chat/completions 在 stream: true 时默认 不 在 SSE 末尾追加内部 event: debug(避免仅按 OpenAI data: JSON 解析的客户端把调试负载误当作补全块并校验失败)。若需要该调试事件(含路由、用量、积分等追溯 JSON),请求头加 X-Flowy-Stream-Debug: 1。
- URL:
/api/v1/user/apiKeys - Method:
POST - Headers:
Authorization: Bearer <C 端 JWT> - Body:
{"name":"名称"} - Success
data:id,name,apiKey(仅此一次返回完整密钥;形式为flowy-后跟仅含0-9、A-Z、a-z的随机段,默认 32 位),createdAt
- URL:
/api/v1/user/apiKeys - Method:
GET - Headers:
Authorization: Bearer <C 端 JWT> - Query:
page(默认1)、pageSize(默认10,最大200) - 说明: 仅返回
status=1(可用)的密钥,按id倒序分页。 - Success
data:list(元素含id,name,apiKey,createdAt,lastUsedAt,status)、total(总条数)
- URL:
/api/v1/user/apiKeys/:id - Method:
PUT - Headers:
Authorization: Bearer <C 端 JWT> - Body:
{"name":"新名称"}
- URL:
/api/v1/user/apiKeys/:id - Method:
DELETE - Headers:
Authorization: Bearer <C 端 JWT> - 说明: 将密钥标记为已删除,并清除 Redis 中该 key → user_id 的缓存
- URL:
/v1/chat/session - Method:
POST - Headers:
Authorization: Bearer <token>
- Content-Type:
application/json - 说明:
- 客户端在调用
/v1/chat/completions、/v1/embeddings、/v1/rerank之前,建议先调用该接口上报当前会话 ID(sessionId),用于后续将模型调用记录按会话聚合与追踪。 - 该接口为高可用设计:无论参数校验失败、Redis 写入失败,接口都会返回 HTTP 200,客户端可继续发起对话请求;是否成功写入由
data.stored表示。 - 多端隔离:当前服务端将
clientId固定为PC(客户端无需上报)。
- 客户端在调用
- Request Body:
{ "sessionId": "sess_20260403_001" } - 字段说明:
sessionId: 会话 ID(必填,去首尾空格;最大长度 128)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "stored": true } }
- Code:
- 返回字段说明:
stored: 是否成功写入当前会话(true表示会话已写入并将用于后续落库归因;false表示未写入但不影响继续调用对话接口)
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
400(登录态异常导致的用户信息不合法)
- Code:
- URL:
/api/v1/device/activate - Method:
POST - Content-Type:
application/json - Headers:
Authorization: Bearer <token>
- Request Body:
- 必填字段:
mac、sn、activateTimestamp、cpuChipId、appVersion - 可选字段:
channel、app(客户端标识:aipc/herdsman/flowymes,未传默认aipc)、osVersion、xpuBrand、publicIP、countryCode、postal、latitude、longitude、isp、timezone、currency等 - 说明:
latitude/longitude支持字符串或数字(例如"39.908823"或39.908823)
{ "mac": "00:1A:2B:3C:4D:5E", "sn": "SN202603030001", "activateTimestamp": 1741052716000, "cpuChipId": "CPU8888888888888888", "appVersion": "v2.5.1", "osVersion": "kernel_4.19.97", "xpuBrand": "NVIDIA", "publicIP": "112.124.xx.xx", "countryCode": "CN", "postal": "100080", "latitude": "39.908823", "longitude": "116.397470", "isp": "China Telecom", "timezone": "GMT+8", "currency": "CNY" } - 必填字段:
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Activate successful" }
- Code:
- Error Response:
- Code:
400(必填字段缺失或格式不正确) - Code:
401(未登录或 token 无效) - Code:
500(激活上报入库失败)
- Code:
- 幂等与去重:
- 服务端按
mac+cpuChipId+channel+clientApp+appVersion+activate_state=1判断是否已激活成功;已存在则返回 200 且不重复插入。 - 同一物理机在不同客户端 App 或不同客户端版本下可各保留一条成功激活记录。
- 服务端按
- URL:
/api/v1/presence/heartbeat - Method:
POST - Content-Type:
application/json(可选;允许空 body) - Headers:
Authorization: Bearer <token>
- 说明:
- 服务端按“用户维度”统计在线数量:同一用户多端在线只记 1 个在线用户
- 在线判断基于滑动窗口:
lastSeen >= serverTime - offlineWindowMs
- Request Body(可选):
clientId: 客户端实例 ID(建议 UUID;用于排查与未来扩展,当前统计按用户维度不依赖该字段)platform: 平台(例如windows/macos/linux)appVersion: 客户端版本号- 字段说明:
clientId:客户端实例标识(可选),当前仅用于回显与后续扩展platform:客户端平台(可选)appVersion:客户端版本号(可选)
{ "clientId": "9f0a6d6b-8b3d-4a1e-8bdf-2b8b0c3f0b9a", "platform": "windows", "appVersion": "v2.5.1" } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "serverTime": "2026-03-12T08:00:00Z", "offlineWindowMs": 90000, "userId": 1, "clientIdAccepted": true } } - 返回字段说明:
serverTime:服务端时间(RFC3339)offlineWindowMs:在线判断窗口(毫秒);若超过该窗口未上报心跳则视为离线userId:当前登录用户 ID(用于排查/回显)clientIdAccepted:请求体是否携带了clientId(用于排查/回显)
- Code:
- Error Response:
- Code:
400(请求体格式不正确) - Code:
401(未登录或 token 无效) - Code:
500(心跳记录失败,例如 Redis 不可用)
- Code:
- URL:
/api/v1/presence/onlineUsers - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 返回当前系统在线用户数(按用户维度统计)
- 在线判断基于滑动窗口:
lastSeen >= serverTime - offlineWindowMs
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "serverTime": "2026-03-12T08:00:00Z", "onlineUsers": 123, "offlineWindowMs": 90000 } } - 返回字段说明:
serverTime:服务端时间(RFC3339)onlineUsers:当前在线用户数(按用户维度)offlineWindowMs:在线判断窗口(毫秒)
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败,例如 Redis 不可用)
- Code:
- URL:
/api/v1/time/verify - Method:
POST - Content-Type:
application/json - Headers:
Authorization: Bearer <token>Accept-Language: 可选;用于错误提示国际化(例如zh-CN、en)
- 说明:
- 客户端上报当前系统时间,服务端与 UTC 下的服务器当前时间比较
- 允许误差:小于等于 3 分钟(含等于)
- 比较时双方时间均归一化为 UTC;客户端传入带时区偏移的 RFC3339 时会先换算为 UTC 再比较
- Request Body:
{ "clientTime": "2026-06-10T12:00:00Z" }clientTime(必填):客户端当前系统时间,RFC3339 或 RFC3339Nano 字符串(建议使用 UTC,例如末尾带Z)
- Success Response(时间正确,误差 ≤ 3 分钟):
- HTTP Status:
200 - Body:
{ "code": 200, "msg": "成功", "data": { "valid": true, "serverTime": "2026-06-10T12:00:01.234567890Z", "driftMs": 1234 } } - 返回字段说明:
valid:固定为true,表示客户端时间与服务器时间一致(在允许误差内)serverTime:服务端当前 UTC 时间(RFC3339Nano)driftMs:客户端时间与服务器时间的绝对偏差(毫秒)
- HTTP Status:
- Error Response:
- Code:
400(clientTime缺失、格式非法,或时间偏差 > 3 分钟)- 格式非法示例:
msg为「参数 clientTime 不合法」(error.invalid_param) - 时间偏差过大示例(中文):
msg为「客户端时间与服务器时间相差超过 3 分钟(当前相差约 4 分钟),请校准系统时间」(error.client_time_out_of_sync)
- 格式非法示例:
- Code:
401(未登录或 token 无效)
- Code:
- 客户端建议:
- 上报前将本地系统时间格式化为 UTC RFC3339(例如
new Date().toISOString()) - 收到
400且error.client_time_out_of_sync对应文案时,提示用户校准操作系统时间后重试
- 上报前将本地系统时间格式化为 UTC RFC3339(例如
- 获取/下单/支付均需先完成登录,拿到
token - 获取可购买套餐列表:
GET /api/v1/plans - 查询可用支付渠道(按商品币种过滤):
GET /api/v1/paymentChannels?itemType=plan&itemId=...&planPeriod=... - 用户选择一个
payChannel后创建订单(幂等):POST /api/v1/orders(响应会直接包含payment支付入口信息) - (可选,兼容旧流程)统一发起支付:
POST /api/v1/orders/:orderNo/pay(服务端按订单pay_channel派发到具体支付渠道;若该订单已有支付入口信息,通常会直接复用返回) - 客户端按
payment//pay返回值展示支付信息:channel=wechatpay:展示codeUrl二维码扫码支付channel=airwallex:使用paymentIntentId/clientSecret走前端/SDK 支付流程
- 支付成功后,支付渠道服务器回调(客户端不调用):
POST /api/v1/pay/wechat/notify/POST /api/v1/pay/airwallex/notify - 客户端可轮询查询订单:
GET /api/v1/orders/:orderNo,直到status=PAID
服务端在回调处理成功后会:
- 将订单状态更新为
PAID - 写入/更新支付流水(tb_payments)
- 发放套餐积分(写入积分流水并增加余额),积分过期时间与订阅到期时间一致
- 写入用户订阅(tb_user_subscriptions),并将旧的
ACTIVE订阅置为EXPIRED - 订阅期内服务端会按月为每个有效个人订阅发放套餐
grantPoints积分(幂等;首月在购买成功时发放,自次月起由定时任务补发)
与套餐类似,区别点:
- 建议通过
GET /api/v1/creditPacks/available获取“当前用户可购买”的加量包(会按用户当前订阅套餐进行过滤) - 创建订单时
itemType=pack - 创建订单响应会直接包含
payment支付入口信息(如需兼容旧流程,可继续调用/api/v1/orders/:orderNo/pay) - 服务端会校验:
- 用户必须存在未过期的
ACTIVE订阅 - 若当前订阅套餐配置了可购加量包白名单,则
packId必须在白名单内
- 用户必须存在未过期的
- 回调成功后仅发放积分(可选过期时间取决于
validDays)
与普通套餐类似,区别点:
- 获取可购买牧马人套餐列表:
GET /api/v1/wranglerPlans - 创建订单时
itemType=wrangler_plan - 支付成功后服务端不会发积分,而是立即创建新的牧马人身份记录
- 若用户原本已有生效中的牧马人身份,新身份会立即覆盖旧身份;旧身份状态会被置为
SUPERSEDED - 退款成功后,对应牧马人身份应立即失效并回收(状态置为
REVOKED)
- 用户先完成登录,拿到
token(创建团队/下单/支付需要;仅查看团队套餐列表不需要) - 团队 owner 创建团队:
POST /api/v1/teams - 团队 owner 添加成员(按用户 ID):
POST /api/v1/teams/:teamId/members - 获取可购买团队套餐列表(免 token):
GET /api/v1/teamPlans - 查询可用支付渠道(按商品币种过滤):
GET /api/v1/paymentChannels?itemType=team_plan&itemId=...&teamId=... - 用户选择一个
payChannel后创建订单(幂等):POST /api/v1/orders,itemType=team_plan,必须携带teamId(响应会直接包含payment支付入口信息) - (可选,兼容旧流程)统一发起支付:
POST /api/v1/orders/:orderNo/pay - 支付成功后,支付渠道服务器回调(客户端不调用):
POST /api/v1/pay/wechat/notify/POST /api/v1/pay/airwallex/notify - 支付入账成功后:
- 服务端会为该团队创建/续费订阅期,并按套餐
baseSeatCount初始化席位 - 服务端会自动为团队成员分配空闲席位(成员加入时优先自动入席;若成员先加入后购套餐/扩容,也会在支付成功后自动补齐入席)
- 团队管理员可查询当前席位:
GET /api/v1/teams/:teamId/seats - (可选)团队管理员可手动调整席位分配:
POST /api/v1/teams/:teamId/seats/:seatNo/assign/POST /api/v1/teams/:teamId/seats/:seatNo/unassign - 订阅期内服务端会按月为每个“有效席位”发放
monthlyGrantPointsPerSeat积分(幂等)
- 服务端会为该团队创建/续费订阅期,并按套餐
- (可选)团队扩容席位:
- 查询可用支付渠道:
GET /api/v1/paymentChannels?itemType=team_seat_expand&itemId=...&teamId=...&seatDelta=...&expandMode=... - 用户选择
payChannel后创建订单(幂等):POST /api/v1/orders,itemType=team_seat_expand,必须携带teamId、seatDelta、expandMode(响应会直接包含payment支付入口信息) - (可选,兼容旧流程)统一发起支付:
POST /api/v1/orders/:orderNo/pay - 支付成功后扩容生效:
expandMode=TEMP时写入订阅期临时扩容;expandMode=PERM时写入团队订阅永久扩容 - 特殊规则(方案 5.2-B):若
expandMode=TEMP的扩容订单在支付成功回调时发现“目标订阅期已过期”,服务端会自动按PERM方式入账(永久扩容)
- 用户在同一时间可以同时拥有个人积分余额与团队席位积分余额(来自其被分配的席位)
- 模型调用发生积分消耗时:优先消耗“席位积分”(若用户有有效席位且有余额),不足部分再消耗个人积分
- 当用户离开席位(被解绑/移出团队/主动退出等)后,不再享受该席位后续发放与余额消耗权限
- 订单状态
status:CREATED/PAYING/PAID/REFUNDED/EXPIRED/CANCELEDREFUNDED:订单已完成全额退款,且本地权益回收已进入成功终态
- 套餐周期
planPeriod:MONTH/HALF_YEAR/YEAR - 商品类型
itemType:plan(个人套餐) /wrangler_plan(牧马人套餐) /pack(积分加量包) /team_plan(团队套餐) /team_seat_expand(团队席位扩容)
- 用户先完成登录,拿到
token - 调用
GET /api/v1/promo/inviteCode获取当前用户的邀请码与邀请链接(不存在会自动生成) - 将
inviteLink分享给被邀请用户(邀请链接 = 官网地址 +inviteCode参数) - 被邀请用户点击邀请链接访问官网,官网从 URL 参数中获取
inviteCode
- 被邀请用户先完成登录,拿到
token - 调用
POST /api/v1/promo/bindInviteCode提交inviteCode - 绑定成功后服务端会写入拉新绑定记录(tb_referral_invites),并给双方各发一张券(tb_user_coupons)
- 绑定结果会返回双方发放的优惠券信息(
inviteeCoupon/inviterCoupon)
- 用户先完成登录,拿到
token - 调用
POST /api/v1/promo/coupons/claim提交templateCode - 服务端会按模板发券(tb_coupon_templates -> tb_user_coupons),并限制同一模板每人最多领取一次
- 调用
GET /api/v1/promo/coupons?status=ALL查询我的券列表 - 选择
status=UNUSED且expiresAt未过期的券,拿到couponId - 创建订单时在
POST /api/v1/orders的请求体携带couponId - 服务端会在创建订单时将该券从
UNUSED置为LOCKED(关联lockedOrderId) - 支付成功后服务端会将该券置为
USED(关联usedOrderId);若订单超时过期则会自动解锁回UNUSED或标记为EXPIRED
- URL:
/api/v1/promo/inviteCode - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 返回当前登录用户的邀请码与邀请链接;若不存在会自动生成
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "inviteCode": "ABCD1234", "inviteLink": "https://claw.flowyaipc.cn/?inviteCode=ABCD1234" } } - 返回字段说明:
inviteCode:当前用户的邀请码,可分享给其他用户inviteLink:邀请链接(官网地址 +inviteCode参数),可直接分享给他人
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(生成/查询失败)
- Code:
- URL:
/api/v1/promo/invitees - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
page: 页码,默认1pageSize: 每页数量,默认10,最大200
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "list": [ { "invitee_user_id": 123, "invitee_email": "user@example.com", "invitee_nickname": "User123", "invite_code": "ABCD1234", "created_at": "2026-03-12T08:00:00Z" } ], "total": 1 } }
- Code:
- 返回字段说明:
list[]:我邀请的新用户列表(基于 tb_referral_invites 绑定记录)list[].invitee_user_id:被邀请用户 IDlist[].invitee_email:被邀请用户邮箱(可能为空字符串)list[].invitee_nickname:被邀请用户昵称list[].invite_code:使用的邀请码list[].created_at:绑定时间(RFC3339)total:总数
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/promo/bindInviteCode - Method:
POST - Headers:
Authorization: Bearer <token>Content-Type: application/json
- Request Body:
{ "inviteCode": "ABCD1234" } - 请求字段说明:
inviteCode:邀请人分享的邀请码(不区分大小写处理由客户端负责;服务端仅做 trim)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "inviterUserId": 123, "inviteeCoupon": { "id": 10001, "templateCode": "REFERRAL_INVITEE_1000", "title": "新人奖励券", "currency": "CNY", "discountCent": 1000, "applicableItemTypes": "all", "status": "UNUSED", "issuedAt": "2026-03-12T08:00:00Z", "expiresAt": "2026-04-11T08:00:00Z", "lockedOrderId": null, "usedOrderId": null }, "inviterCoupon": { "id": 10002, "templateCode": "REFERRAL_INVITER_1000", "title": "邀请奖励券", "currency": "CNY", "discountCent": 1000, "applicableItemTypes": "all", "status": "UNUSED", "issuedAt": "2026-03-12T08:00:00Z", "expiresAt": "2026-04-11T08:00:00Z", "lockedOrderId": null, "usedOrderId": null } } }
- Code:
- 返回字段说明:
inviterUserId:邀请人用户 IDinviteeCoupon:被邀请人领取到的券inviterCoupon:邀请人领取到的券
- Error Response:
- Code:
400invalid inviteCode:邀请码不存在cannot invite yourself:不能绑定自己的邀请码already joined referral activity:该用户已绑定过邀请码(每个用户只能绑定一次)
- Code:
401(未登录或 token 无效) - Code:
500(绑定/发券失败)
- Code:
- URL:
/api/v1/promo/coupons - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
status:ALL/UNUSED/LOCKED/USED/EXPIRED/VOID(默认ALL)applicableItemTypes:可选。指定只返回可适用于所列商品类型的优惠券;多个类型用英文逗号分隔,或与status等并列重复传参(如?applicableItemTypes=plan&applicableItemTypes=pack)。可取值与每券applicableItemTypes段一致,例如plan/pack/team_plan等。不传则返回全部(仍受status影响)。
- 说明:
- 查询时会顺便将当前用户已过期且仍为
UNUSED/LOCKED的券标记为EXPIRED
- 查询时会顺便将当前用户已过期且仍为
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "list": [ { "id": 10001, "templateCode": "REFERRAL_INVITEE_1000", "title": "新人奖励券", "currency": "CNY", "discountCent": 1000, "applicableItemTypes": "all", "status": "UNUSED", "issuedAt": "2026-03-12T08:00:00Z", "expiresAt": "2026-04-11T08:00:00Z", "lockedOrderId": null, "usedOrderId": null } ] } }
- Code:
- 返回字段说明:
list[]:优惠券数组list[].id:用户券 ID(下单时couponId传这个值)list[].templateCode:模板编码快照list[].title:标题快照list[].currency:币种list[].discountCent:抵扣金额(分)list[].applicableItemTypes:适用商品类型(all/plan/pack,可为逗号分隔)list[].status:UNUSED/LOCKED/USED/EXPIRED/VOIDlist[].issuedAt:发放时间(RFC3339)list[].expiresAt:过期时间(RFC3339)list[].lockedOrderId:锁定的订单 ID(仅当status=LOCKED时可能非空)list[].usedOrderId:使用的订单 ID(仅当status=USED时可能非空)
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/promo/coupons/claim - Method:
POST - Headers:
Authorization: Bearer <token>Content-Type: application/json
- Request Body:
{ "templateCode": "PROMO_WELCOME_500" } - 请求字段说明:
templateCode:优惠券模板编码(对应 tb_coupon_templates.code)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 10003, "templateCode": "PROMO_WELCOME_500", "title": "活动优惠券", "currency": "CNY", "discountCent": 500, "applicableItemTypes": "all", "status": "UNUSED", "issuedAt": "2026-03-12T08:00:00Z", "expiresAt": "2026-03-19T08:00:00Z", "lockedOrderId": null, "usedOrderId": null } }
- Code:
- Error Response:
- Code:
400already claimed:同一模板每个用户最多领取一次coupon template not found:模板不存在或未启用
- Code:
401(未登录或 token 无效) - Code:
500(领取失败)
- Code:
- URL:
/api/v1/plans - Method:
GET - Query Parameters:
planPeriod: 计费周期筛选(可选)。支持MONTH/HALF_YEAR/YEARcurrency: 币种筛选(可选,大小写不敏感)。例如CNY/USD/JPYexternalChannelCode: 三方渠道(可选)。用于按external_channel_code过滤可售目录;未登录且不传时默认flowyexternalChannelCode: 三方渠道(可选,默认空字符串)。用于按external_channel_code过滤可售目录
- 说明:
- 同一套餐的不同计费周期按多行返回,通过
planPeriod区分
- 同一套餐的不同计费周期按多行返回,通过
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 1, "code": "basic", "planPeriod": "MONTH", "name": "基础月卡", "description": "适合轻度使用", "currency": "CNY", "currentPriceCent": 1990, "originalPriceCent": 2990, "discountEnabled": true, "discountRate": 0.95, "firstMonthDiscount": 1, "firstMonthPriceCent": 990, "userFirstMonthDiscountEligible": true, "durationDays": 30, "durationMonths": 1, "grantPoints": 100000, "modelIds": ["qwen-long"], "sort": 100, "extJson": "{}", "isHot": true, "benefitList": ["不限模型调用次数", "专属高速通道"], "isCurrent": false }, { "id": 2, "code": "basic", "planPeriod": "YEAR", "name": "基础年卡", "description": "适合长期使用", "currency": "CNY", "currentPriceCent": 19900, "originalPriceCent": 0, "discountEnabled": false, "discountRate": 1, "firstMonthDiscount": 0, "firstMonthPriceCent": 0, "userFirstMonthDiscountEligible": false, "durationDays": 365, "durationMonths": 12, "grantPoints": 1200000, "modelIds": ["qwen-long"], "sort": 90, "extJson": "{}", "isHot": false, "benefitList": ["不限模型调用次数"], "isCurrent": true } ] }
- Code:
- Error Response:
- Code:
400(planPeriod不合法) - Code:
401(未登录或 token 无效) - Code:
500(获取套餐失败)
- Code:
- URL:
/api/v1/wranglerPlans - Method:
GET - Query Parameters:
planPeriod: 计费周期筛选(可选)。支持MONTH/HALF_YEAR/YEARcurrency: 币种筛选(可选,大小写不敏感)。例如CNY/USDexternalChannelCode: 三方渠道(可选,默认空字符串)。用于按external_channel_code过滤可售目录
- 说明:
- 仅返回已上架(Published)的牧马人套餐
- 牧马人套餐不发积分,仅用于授予对应
code身份 - 若当前用户已登录,字段
isCurrent会标记是否为当前生效中的牧马人身份
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 8, "code": "wrangler_gold", "planPeriod": "MONTH", "name": "牧马人金卡", "nameEn": "Wrangler Gold", "description": "高级身份权益", "descriptionEn": "Advanced identity privileges", "currency": "CNY", "currentPriceCent": 29900, "originalPriceCent": 39900, "discountEnabled": true, "discountRate": 0.75, "durationDays": 30, "durationMonths": 1, "sort": 100, "isHot": true, "benefitList": ["身份特权A", "身份特权B"], "benefitListEn": ["Privilege A", "Privilege B"], "extJson": "{}", "isCurrent": true } ] }
- Code:
- Error Response:
- Code:
500(获取牧马人套餐失败)
- Code:
- URL:
/api/v1/teamPlans - Method:
GET - Query Parameters:
currency: 币种筛选(可选,大小写不敏感)。例如CNY/USD/JPYexternalChannelCode: 三方渠道(可选,默认空字符串)。用于按external_channel_code过滤可售目录
- 说明:
- 仅返回已上架(Published)的团队套餐
- 团队套餐按“周期总价”返回;下单无需传
subscriptionMonths,有效期与总价由套餐周期决定
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 1, "code": "team_basic", "planPeriod": "MONTH", "name": "团队基础版", "nameEn": "Team Basic", "description": "适合小团队", "descriptionEn": "For small teams", "currency": "CNY", "currentPriceCent": 9900, "originalPriceCent": 12900, "discountEnabled": true, "discountRate": 0.9, "addonSeatPriceCentPerMonthPerm": 1200, "addonSeatPriceCentPerMonthTemp": 1500, "durationDays": 30, "baseSeatCount": 5, "monthlyGrantPointsPerSeat": 50000, "dailyCheckInGrantPoints": 200, "benefitList": ["共享席位", "团队管理"], "benefitListEn": ["Shared seats", "Team management"], "sort": 100 } ] }
- Code:
- Error Response:
- Code:
500(获取团队套餐失败)
- Code:
- URL:
/api/v1/creditPacks - Method:
GET - Query Parameters:
currency: 币种筛选(可选,大小写不敏感)。例如CNY/USD/JPYexternalChannelCode: 三方渠道(可选,默认空字符串)。用于按external_channel_code过滤可售目录
- 说明:
- 仅返回已上架(Published)的加量包
- 不会按用户订阅套餐做过滤(适合未登录场景或兼容旧客户端)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 10, "code": "points_10w", "name": "10万积分包", "description": "有效期一年", "currency": "CNY", "priceCent": 990, "points": 100000, "validDays": 365, "sort": 100, "extJson": "{}" } ] }
- Code:
- Error Response:
- Code:
500(获取加量包失败)
- Code:
- URL:
/api/v1/creditPacks/available - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
currency: 币种筛选(可选,大小写不敏感)。例如CNY/USD/JPY
- 说明:
- 会读取用户当前未过期的
ACTIVE订阅套餐,并按套餐配置的“可购加量包”白名单进行过滤 - 若用户无有效订阅:返回空数组
[] - 若套餐未配置白名单:等同于
GET /api/v1/creditPacks(返回所有已上架加量包)
- 会读取用户当前未过期的
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 10, "code": "points_10w", "name": "10万积分包", "description": "有效期一年", "currency": "CNY", "priceCent": 990, "points": 100000, "validDays": 365, "sort": 100, "extJson": "{}" } ] }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(获取可购加量包失败)
- Code:
- URL:
/api/v1/credits/balance - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 返回当前登录用户“可用余额”(会自动排除已过期积分批次)
- 字段
balance为整数积分
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "balance": 12345 } }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/credits/consumptionStats - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
dim: 统计维度,可选day/week/month(默认day)startTime: 起始时间(可选)endTime: 结束时间(可选)
- 时间格式:
startTime/endTime支持以下任意一种格式:- 毫秒时间戳(例如
1710000000000) - RFC3339(例如
2026-03-10T12:00:00+08:00) 2006-01-02 15:04:052006-01-02
- 毫秒时间戳(例如
- 当使用
2006-01-02(仅日期)作为endTime时,服务端会将其视为“包含当天”的结束时间。
- 默认区间规则(当
startTime/endTime都不传时):day:返回最近 7 天(含今天)week:返回最近 4 周(ISO 周,周一为起始)month:返回当年 12 个月(YYYY-01~YYYY-12)
- 返回说明:
list会补齐区间内缺失的 bucket(消耗为 0)bucket格式:day:YYYY-MM-DDweek:YYYY-Www(ISO 周,例如2026-W10)month:YYYY-MM
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "dim": "day", "startTime": "2026-03-04T00:00:00+08:00", "endTime": "2026-03-10T23:59:59.999+08:00", "total": 120, "list": [ { "bucket": "2026-03-04", "consumed": 0 }, { "bucket": "2026-03-05", "consumed": 20 }, { "bucket": "2026-03-06", "consumed": 0 }, { "bucket": "2026-03-07", "consumed": 30 }, { "bucket": "2026-03-08", "consumed": 10 }, { "bucket": "2026-03-09", "consumed": 0 }, { "bucket": "2026-03-10", "consumed": 60 } ] } }
- Code:
- Error Response:
- Code:
400(时间参数不合法或区间不合法) - Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/credits/checkin - Method:
POST - Headers:
Authorization: Bearer <token>
- 说明:
- 每个用户每天最多签到一次(以客户端传入的
timeZone对应的日期为准) - 首次签到会赠送
200积分;重复签到会返回alreadyCheckedIn=true且grantedPoints=0 - 签到赠送的积分会在该用户当日
24:00(签到时区)过期 - 返回字段
balance为签到后的“可用余额”
- 每个用户每天最多签到一次(以客户端传入的
- Query Parameters:
timeZone: 时区(可选,IANA 名称,例如Asia/Shanghai)
- Request Body(可选):
{ "timeZone": "Asia/Shanghai" } - Success Response:
- Code:
200 - Body(首次签到示例):
{ "code": 200, "msg": "Success", "data": { "alreadyCheckedIn": false, "grantedPoints": 200, "balance": 10200, "checkInAt": "2026-03-11T08:09:51.123+08:00", "dayKey": 20260311 } } - Body(重复签到示例):
{ "code": 200, "msg": "Success", "data": { "alreadyCheckedIn": true, "grantedPoints": 0, "balance": 10200, "checkInAt": "2026-03-11T08:09:51.123+08:00", "dayKey": 20260311 } }
- Code:
- Error Response:
- Code:
400(timeZone不合法) - Code:
401(未登录或 token 无效) - Code:
500(签到失败)
- Code:
- URL:
/api/v1/credits/checkinRecords - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 仅返回“当前登录用户”指定月份内的签到记录
- 仅返回该月已签到的日期列表,以及对应发放积分
- Query Parameters:
month: 目标年月,格式固定为yyyy-MM,例如2026-03
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "month": "2026-03", "totalCheckInDays": 18, "list": [ { "date": "2026-03-10", "day": 10, "dayKey": 20260310, "checkInAt": "2026-03-10T08:05:10.000+08:00", "grantedPoints": 200, "timeZone": "Asia/Shanghai" }, { "date": "2026-03-11", "day": 11, "dayKey": 20260311, "checkInAt": "2026-03-11T08:09:51.123+08:00", "grantedPoints": 200, "timeZone": "Asia/Shanghai" } ] } }
- Code:
- Error Response:
- Code:
400(month缺失或格式不是yyyy-MM) - Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/credits/redeemCode - Method:
POST - Headers:
Authorization: Bearer <token>Content-Type: application/json
- 说明:
- 使用“运营平台创建的积分兑换码”给当前用户增加积分
- 同一用户对同一兑换码只能兑换一次(服务端通过唯一约束保证)
- 返回字段
balance为兑换后的“可用余额”
- Request Body:
{ "redeemCode": "ABCD2EFG3HJK" } - 请求字段说明:
redeemCode:积分兑换码(字符串,必填)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "grantedPoints": 2000, "balance": 10200 } }
- Code:
- 返回字段说明:
grantedPoints:本次兑换发放的积分数额balance:兑换后的可用余额
- Error Response:
- Code:
400invalid redeem code:兑换码为空/格式不合法redeem code not found:兑换码不存在redeem code disabled:兑换码被禁用/数据异常redeem code exhausted:兑换码已被兑换完(达到最大次数)
- Code:
409(redeem code already redeemed:该用户已兑换过该兑换码) - Code:
401(未登录或 token 无效) - Code:
500(兑换失败)
- Code:
- URL:
/api/v1/credits/ledger - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
page: 页码(从 1 开始,默认 1)pageSize: 每页条数(默认 10,最大 200)startTime: 起始时间(可选)endTime: 结束时间(可选)
- 时间格式:
startTime/endTime支持以下任意一种格式:- 毫秒时间戳(例如
1710000000000) - RFC3339(例如
2026-03-10T12:00:00+08:00) 2006-01-02 15:04:052006-01-02
- 毫秒时间戳(例如
- 当使用
2006-01-02(仅日期)作为startTime时,服务端会将其视为当天00:00:00 - 当使用
2006-01-02(仅日期)作为endTime时,服务端会将其视为“包含当天”的结束时间
- 返回说明:
balance:当前可用余额(会自动排除已过期积分批次)delta:本条积分变动值(正数为增加,负数为消耗)explanation:对本条变动的解释文案expireAt:仅对“增加积分且有过期时间”的记录返回,表示该批次积分的过期时间
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "balance": 10200, "total": 2, "list": [ { "id": 1001, "delta": -60, "reason": "consume_model", "explanation": "模型调用消耗(gpt-4.1)", "occurredAt": "2026-03-11T10:01:02+08:00" }, { "id": 999, "delta": 200, "reason": "daily_checkin", "explanation": "每日签到赠送", "occurredAt": "2026-03-11T08:09:51+08:00" } ] } }
- Code:
- Error Response:
- Code:
400(时间参数不合法或区间不合法) - Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/credits/usageByType - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
includeTeamSeat: 是否包含团队席位积分,默认1。可选:1/0、true/false
- 说明:
- 仅返回“当前有效”的积分使用情况:
- 个人积分:只统计未过期的积分批次(
tb_credit_batches) - 团队席位积分:只统计当前被分配且订阅期未过期的席位积分
- 个人积分:只统计未过期的积分批次(
- 汇总字段口径:
used = total - remaining
- 仅返回“当前有效”的积分使用情况:
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "serverTime": "2026-03-19T10:00:00Z", "includeTeamSeat": true, "list": [ { "type": "DAILY_CHECKIN", "title": "每日签到积分", "total": 400, "used": 120, "remaining": 280, "buckets": [ { "expireAt": "2026-03-19T23:59:59+08:00", "total": 200, "used": 0, "remaining": 200, "sourceCode": "daily_checkin" } ] }, { "type": "TEAM_SEAT", "title": "团队席位积分", "total": 50000, "used": 2000, "remaining": 48000, "buckets": [ { "expireAt": "2026-04-01T00:00:00+08:00", "total": 50000, "used": 2000, "remaining": 48000, "teamId": 100, "periodId": 200 } ] } ] } }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/paymentChannels - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Params:
itemType:plan/wrangler_plan/pack/team_plan/team_seat_expanditemId: 商品 IDplanPeriod: 可选,仅itemType=plan时用于按周期切换到目标套餐teamId: 必填,仅itemType=team_plan/team_seat_expandseatDelta: 必填,仅itemType=team_seat_expandexpandMode: 必填,仅itemType=team_seat_expand(PERM/TEMP)
- 说明:
- 服务端会基于商品快照里的
currency返回该币种下“可用且已启用”的支付渠道列表(按配置排序)
- 服务端会基于商品快照里的
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "code": "wechatpay", "name": "微信支付" }, { "code": "airwallex", "name": "Airwallex" } ] }
- Code:
- Error Response:
- Code:
400(参数不合法 / teamId 缺失等) - Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/orders - Method:
POST - Headers:
Authorization: Bearer <token>
- Content-Type:
application/json - Request Body:
{ "itemType": "wrangler_plan", "itemId": 8, "planPeriod": "MONTH", "payChannel": "wechatpay", "idempotencyKey": "wrangler-order-20260325-0001" }- 普通套餐下单示例:
{ "itemType": "plan", "itemId": 1, "planPeriod": "MONTH", "payChannel": "wechatpay", "teamId": 0, "idempotencyKey": "3d3cfcc3e0d942c7a7c726ad9b1de2aa" }- 团队套餐下单示例:
{ "itemType": "team_plan", "itemId": 1, "teamId": 100, "payChannel": "wechatpay", "idempotencyKey": "idem-team-20260317-0001" }- 团队席位扩容下单示例:
{ "itemType": "team_seat_expand", "itemId": 1, "teamId": 100, "seatDelta": 3, "expandMode": "TEMP", "payChannel": "wechatpay", "idempotencyKey": "idem-team-seat-20260317-0001" }itemType:plan(个人套餐)/wrangler_plan(牧马人套餐)/pack(积分加量包)/team_plan(团队套餐)/team_seat_expand(团队席位扩容)planPeriod: 当itemType=plan或itemType=wrangler_plan时生效,可选:MONTH/HALF_YEAR/YEAR- 不传:按
itemId对应套餐本身的planPeriod下单 - 传且与
itemId的planPeriod不同:服务端会按itemId对应套餐的code+ 目标planPeriod自动切换到对应周期的套餐下单
- 不传:按
teamId: 仅当itemType=team_plan时必填,表示团队 ID(/api/v1/teams创建后返回的 ID)seatDelta: 仅当itemType=team_seat_expand时必填,表示新增席位数(正整数)expandMode: 仅当itemType=team_seat_expand时必填,可选:PERM(永久扩容)/TEMP(临时扩容)- 方案 5.2-B:当
expandMode=TEMP的订单在支付成功回调时发现“目标订阅期已过期”,服务端会自动按PERM方式入账(永久扩容)
- 方案 5.2-B:当
payChannel: 用户选择的支付渠道编码(例如wechatpay/airwallex),建议先通过GET /api/v1/paymentChannels查询后选择idempotencyKey: 客户端生成的幂等键,用于重试不重复下单(建议 UUID/随机串)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 1, "order_no": "OPL260305034500123A1B2C3D4E5F67", "user_id": 1, "item_type": "plan", "item_id": 1, "title": "基础月卡", "currency": "CNY", "amount_cent": 1990, "status": "CREATED", "pay_channel": "wechatpay", "idempotency_key": "3d3cfcc3e0d942c7a7c726ad9b1de2aa", "expires_at": "2026-03-05T12:00:00+08:00", "paid_at": null, "created_at": "2026-03-05T11:45:00+08:00", "updated_at": "2026-03-05T11:45:00+08:00", "titleEn": "Basic Monthly", "subscriptionPeriodMonths": 1, "payment": { "channel": "wechatpay", "codeUrl": "weixin://wxpay/bizpayurl?pr=xxxxxx" } } }
- Code:
- Notes:
order_no采用固定 32 位格式:O+ 2 位业务类型码 + 15 位 UTC 时间(yyMMddHHmmssSSS)+ 14 位随机大写十六进制尾码subscriptionPeriodMonths:plan/wrangler_plan按planPeriod/durationDays折算;pack按validDays折算;team_plan按团队套餐的planPeriod/durationDays折算- 创建订单响应包含
payment支付入口信息:channel=wechatpay:返回codeUrlchannel=airwallex:返回paymentIntentId与clientSecret
- 兼容旧流程:仍可调用
POST /api/v1/orders/:orderNo/pay获取同结构的支付入口信息
- Error Response:
- Code:
400(请求体不合法) - Code:
401(未登录或 token 无效) - Code:
500- 创建套餐订单失败(套餐不存在或未上架 / 周期不合法等)
- 创建加量包订单失败(加量包不存在或未上架 / 用户无有效订阅 / 加量包不在套餐允许范围内等)
- 创建团队套餐订单失败(团队不存在/无权限 / 团队套餐不存在或未上架 / 套餐周期不合法等)
- Code:
- URL:
/api/v1/orders - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Params:
page: 页码(默认1)pageSize: 每页条数(默认10,最大200)status: 订单状态筛选(可选)。支持单值或逗号分隔多值,例如:PAID、REFUNDED或CREATED,PAYINGitemType: 商品类型筛选(可选)。支持单值或逗号分隔多值,例如:plan或plan,wrangler_plan,pack,team_plan,team_seat_expand
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "list": [ { "id": 1, "order_no": "OPL260305034500123A1B2C3D4E5F67", "item_type": "plan", "item_id": 1, "title": "基础月卡", "item_snapshot_json": "{\"itemType\":\"plan\",\"itemId\":1,\"title\":\"基础月卡\",\"currency\":\"CNY\",\"amountCent\":1990,\"grantPoints\":100000,\"durationDays\":30,\"planPeriod\":\"MONTH\",\"validDays\":0,\"modelIds\":[\"qwen-long\"]}", "currency": "CNY", "amount_cent": 1990, "status": "PAID", "expires_at": "2026-03-05T12:00:00+08:00", "paid_at": "2026-03-05T11:46:12+08:00", "created_at": "2026-03-05T11:45:00+08:00", "updated_at": "2026-03-05T11:46:12+08:00", "pay_channel": "wechat", "pay_status": "SUCCESS", "pay_code_url": null, "invoice_eligible": true } ], "total": 1 } } invoice_eligible(boolean):当前是否允许为该订单提交开票申请。为true当且仅当:订单状态为PAID;不存在进行中的退款(退款状态为CREATED/PROCESSING/SUCCESS);且不存在处于PENDING/PROCESSING/COMPLETED的开票申请(若仅有REJECTED或CANCELLED的申请,或尚未申请过,则为true)。- 当订单完成全额退款后,订单主状态会更新为
REFUNDED
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/orders/:orderNo - Method:
GET - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 1, "order_no": "OPL260305034500123A1B2C3D4E5F67", "user_id": 1, "item_type": "plan", "item_id": 1, "title": "基础月卡", "item_snapshot_json": "{\"itemType\":\"plan\",\"itemId\":1,\"title\":\"基础月卡\",\"currency\":\"CNY\",\"amountCent\":1990,\"grantPoints\":100000,\"durationDays\":30,\"planPeriod\":\"MONTH\",\"validDays\":0,\"modelIds\":[\"qwen-long\"]}", "currency": "CNY", "amount_cent": 1990, "status": "PAID", "idempotency_key": "3d3cfcc3e0d942c7a7c726ad9b1de2aa", "client_ip": "127.0.0.1", "expires_at": "2026-03-05T12:00:00+08:00", "paid_at": "2026-03-05T11:46:12+08:00", "created_at": "2026-03-05T11:45:00+08:00", "updated_at": "2026-03-05T11:46:12+08:00" } }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败或订单不存在)
- Code:
- URL:
/api/v1/orders/byOrderNo - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Params:
orderNo: 订单编号(必填)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 1, "order_no": "OPL260305034500123A1B2C3D4E5F67", "user_id": 1, "item_type": "plan", "item_id": 1, "title": "基础月卡", "item_snapshot_json": "{\"itemType\":\"plan\",\"itemId\":1,\"title\":\"基础月卡\",\"currency\":\"CNY\",\"amountCent\":1990,\"grantPoints\":100000,\"durationDays\":30,\"planPeriod\":\"MONTH\",\"validDays\":0,\"modelIds\":[\"qwen-long\"]}", "currency": "CNY", "amount_cent": 1990, "status": "CREATED", "idempotency_key": "3d3cfcc3e0d942c7a7c726ad9b1de2aa", "client_ip": "127.0.0.1", "expires_at": "2026-03-05T12:00:00+08:00", "paid_at": null, "created_at": "2026-03-05T11:45:00+08:00", "updated_at": "2026-03-05T11:45:00+08:00", "subscriptionPeriodMonths": 1, "payment": { "channel": "wechatpay", "codeUrl": "weixin://wxpay/bizpayurl?pr=xxxxxx" } } }
- Code:
- Notes:
subscriptionPeriodMonths:plan/wrangler_plan按planPeriod/durationDays折算;pack按validDays折算;team_plan按团队套餐的planPeriod/durationDays折算- 当订单未支付且未过期(通常
status=CREATED/PAYING且now < expires_at)时,响应会尽量包含payment支付入口信息,结构与POST /api/v1/orders/:orderNo/pay相同:channel=wechatpay:返回codeUrlchannel=airwallex:返回paymentIntentId与clientSecret
- Error Response:
- Code:
400(orderNo 缺失或不合法) - Code:
401(未登录或 token 无效) - Code:
500(查询失败或订单不存在)
- Code:
- URL:
/api/v1/orders/:orderNo/refundPreview - Method:
GET - Headers:
Authorization: Bearer <token>
- 说明:
- 仅允许查询“当前登录用户自己的订单”
- 该接口仅做退款资格预检,不会真正发起退款
- 当前支持的退款订单类型:
plan、pack - 当前支持的退款渠道:
wechatpay、airwallex - 若已存在状态为
CREATED/PROCESSING/SUCCESS的退款单,则canRefund=false - 若订单发放的积分已被消耗,则
canRefund=false
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "orderNo": "OPL260305034500123A1B2C3D4E5F67", "itemType": "plan", "channel": "wechatpay", "currency": "CNY", "refundAmountCent": 1990, "totalAmountCent": 1990, "grantPoints": 100000, "remainingPoints": 100000, "usedPoints": 0, "canRefund": true, "reasonCode": "", "reason": "", "existingRefundNo": "", "existingRefundStatus": "" } }
- Code:
- 字段说明:
grantPoints: 该订单原始发放积分数remainingPoints: 当前仍可回收的剩余积分usedPoints: 已使用积分;大于0时通常不可退款canRefund: 是否允许继续调用退款接口reasonCode: 不可退款时的机器可读原因码existingRefundNo/existingRefundStatus: 当已存在退款单时返回
- 常见
reasonCode:order_not_paid: 订单未支付channel_unsupported: 支付渠道暂不支持退款refund_exists: 已存在退款单item_type_unsupported: 商品类型暂不支持退款grant_missing: 未找到该订单的发放记录credits_used: 发放积分已被使用subscription_missing: 未找到订阅记录subscription_changed: 订阅状态已变化
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
404(订单不存在,或不属于当前登录用户) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/orders/:orderNo/refund - Method:
POST - Headers:
Authorization: Bearer <token>Content-Type: application/json
- 说明:
- 仅允许对“当前登录用户自己的订单”发起退款
- 服务端会先执行与
refundPreview相同的资格校验,通过后创建退款单并调用原支付渠道退款 - 当前仅支持全额退款
- 退款审计信息会写入
tb_refunds、tb_refund_events - 当支付渠道已在外部后台完成全额退款、再次发起退款时,若渠道返回“订单已全额退款”,服务端会将其视为成功退款并继续执行本地权益回收与状态对齐
- Request Body:
{ "reason": "用户申请退款" } - 请求字段说明:
reason: 退款原因(可选)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "refundNo": "RPL260305114612123A1B2C3D4E5F67", "status": "PROCESSING" } }
- Code:
- 字段说明:
refundNo: 系统退款单号status: 当前退款状态,常见值为PROCESSING/SUCCESS/FAILED
- Error Response:
- Code:
400(请求体不合法) - Code:
401(未登录或 token 无效) - Code:
404(订单不存在,或不属于当前登录用户) - Code:
409(订单当前不可退款,或支付渠道不支持退款) - Code:
500(发起退款失败)
- Code:
- URL:
/api/v1/orders/:orderNo/pay/wechat - Method:
POST - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "codeUrl": "weixin://wxpay/bizpayurl?pr=xxxxxx" } }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(发起支付失败 / 订单已过期 / 订单已支付 / 支付未启用)
- Code:
- URL:
/api/v1/orders/:orderNo/pay - Method:
POST - Headers:
Authorization: Bearer <token>
- 说明:
- 服务端根据订单
pay_channel派发到对应支付渠道 - 该接口用于兼容旧客户端/旧流程;新流程可直接使用
POST /api/v1/orders响应中的payment
- 服务端根据订单
- Success Response:
- Code:
200 - Body:
或:
{ "code": 200, "msg": "Success", "data": { "channel": "wechatpay", "codeUrl": "weixin://wxpay/bizpayurl?pr=xxxxxx" } }{ "code": 200, "msg": "Success", "data": { "channel": "airwallex", "paymentIntentId": "int_***", "clientSecret": "int_***_secret_***" } }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(发起支付失败 / 订单已过期 / 订单已支付 / 支付未启用 / 渠道不匹配)
- Code:
- URL:
/api/v1/pay/wechat/notify - Method:
POST - Headers:
- 微信支付 v3 回调签名头(如
Wechatpay-Serial、Wechatpay-Signature、Wechatpay-Timestamp、Wechatpay-Nonce等)
- 微信支付 v3 回调签名头(如
- Content-Type:
application/json - Success Response:
- Code:
200 - Body:
{ "code": "SUCCESS", "message": "OK" }
- Code:
- Error Response:
- Code:
400 - Body:
{ "code": "FAIL", "message": "xxx" } - 说明:回调具备幂等性,微信可能重复推送,同一订单只会入账一次。
- Code:
- URL:
/api/v1/pay/wechat/refundNotify - Method:
POST - Headers:
- 微信支付 v3 退款回调签名头(如
Wechatpay-Serial、Wechatpay-Signature、Wechatpay-Timestamp、Wechatpay-Nonce等)
- 微信支付 v3 退款回调签名头(如
- Content-Type:
application/json - 说明:
- 该接口仅由微信支付服务器调用,客户端不直接调用
- 服务端验签、解密后会更新退款单状态,并将原始回调落库到
tb_refunds.notify_raw与tb_refund_events - 当退款首次进入
SUCCESS时,会执行一次权益回收,并将订单状态更新为REFUNDED;重复回调不会重复回收
- Success Response:
- Code:
200 - Body:
{ "code": "SUCCESS", "message": "OK" }
- Code:
- Error Response:
- Code:
400 - Body:
{ "code": "FAIL", "message": "xxx" }
- Code:
- URL:
/api/v1/pay/airwallex/notify - Method:
POST - Headers:
x-timestampx-signature
- Content-Type:
application/json - 说明:
- 该接口仅由 Airwallex 服务器调用,客户端不直接调用
- 服务端会校验来源 IP、时间戳和签名
- 已处理的支付事件:
payment_intent.succeededpayment_intent.succeeded.v1
- 已处理的退款事件:
refund.receivedrefund.acceptedrefund.settledrefund.failed
- 退款事件会更新
tb_refunds与tb_refund_events;当退款首次进入成功态时,会执行一次权益回收,并将订单状态更新为REFUNDED
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success" }
- Code:
- Error Response:
- Code:
400(签名非法、时间戳非法、IP 不允许、事件数据非法) - Code:
500(服务端处理失败)
- Code:
- URL:
/api/v1/invoiceRequests - Method:
POST - Headers:
Authorization: Bearer <token>Content-Type: application/json
- 说明:
- 仅允许为“当前登录用户自己的已支付订单”提交开票申请
- 若订单已退款/退款中,不允许提交
- 若该订单已存在状态为
PENDING/PROCESSING/COMPLETED的开票申请,不允许再次提交(409) - 若该订单仅有
REJECTED或CANCELLED的申请,允许再次提交:服务端会复用同一条申请记录,更新内容并重新置为PENDING(申请单号request_no会更新) - 单位抬头建议先调用
GET /api/v1/companySearch/suggestions做企业名称联想,再把选中的companyName/taxpayerNo回填后提交
- Request Body(个人抬头示例):
{ "orderNo": "OPL260305034500123A1B2C3D4E5F67", "invoiceTitleType": "PERSONAL", "invoiceKind": "ELECTRONIC_COMMON", "personalName": "张三", "receiverEmail": "user@example.com", "remark": "请尽快发送" } - Request Body(单位抬头示例):
{ "orderNo": "OPL260305034500123A1B2C3D4E5F67", "invoiceTitleType": "COMPANY", "invoiceKind": "ELECTRONIC_COMMON", "companyName": "杭州示例科技有限公司", "taxpayerNo": "91330100MA12345678", "receiverEmail": "finance@example.com", "remark": "用于报销" } - 请求字段说明:
orderNo: 订单号(必填)invoiceTitleType: 抬头类型(必填),支持PERSONAL/COMPANYinvoiceKind: 发票类型(必填),当前支持ELECTRONIC_COMMON/PAPER_COMMON/VAT_SPECIALpersonalName: 个人抬头时的开票姓名(个人抬头必填其一:与titleName二选一即可;若同时传,优先使用本字段)titleName: 个人抬头时亦可作为开票姓名传入(与personalName二选一);单位抬头时由服务端写入为企业名称,客户端无需传companyName: 企业名称(单位抬头必填)taxpayerNo: 纳税人识别号/统一社会信用代码(单位抬头必填)receiverEmail: 接收邮箱(必填)remark: 发票备注(可选)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 1, "request_no": "IV260401102030123A1B2C3D4E5F67", "order_no": "OPL260305034500123A1B2C3D4E5F67", "invoice_title_type": "COMPANY", "invoice_kind": "ELECTRONIC_COMMON", "title_name": "杭州示例科技有限公司", "company_name": "杭州示例科技有限公司", "taxpayer_no": "91330100MA12345678", "receiver_email": "finance@example.com", "remark": "用于报销", "status": "PENDING", "created_at": "2026-04-01T10:20:30+08:00" } }
- Code:
- Error Response:
- Code:
400invoice order not foundinvoice order is not paidinvoice order is refunded or refundinginvalid invoice title typeinvalid invoice kindinvalid receiver emailcompany name is requiredtaxpayer number is required- 个人抬头未提供开票姓名(
personalName与titleName均为空)
- Code:
401(未登录或 token 无效) - Code:
409(invoice request already exists:已有未驳回且未结束的有效申请,见上文说明) - Code:
500(提交失败)
- Code:
- URL:
/api/v1/invoiceRequests - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
page: 页码(默认1)pageSize: 每页条数(默认10,最大200)orderNo: 订单号筛选(可选)status: 状态筛选(可选),支持PENDING/PROCESSING/COMPLETED/REJECTED/CANCELLED
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "list": [ { "id": 1, "request_no": "IV260401102030123A1B2C3D4E5F67", "order_no": "OPL260305034500123A1B2C3D4E5F67", "order_title": "基础月卡", "order_amount_cent": 1990, "currency": "CNY", "invoice_title_type": "COMPANY", "invoice_kind": "ELECTRONIC_COMMON", "title_name": "杭州示例科技有限公司", "company_name": "杭州示例科技有限公司", "taxpayer_no": "91330100MA12345678", "receiver_email": "finance@example.com", "remark": "用于报销", "status": "PROCESSING", "rejection_reason": "", "process_remark": "运营已开始处理", "created_at": "2026-04-01T10:20:30+08:00", "processed_at": "2026-04-01T11:00:00+08:00", "completed_at": null, "sent_email_at": null } ], "total": 1 } }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/companySearch/suggestions - Method:
GET - Headers:
Authorization: Bearer <token>
- Query Parameters:
keyword: 企业名称关键字(建议至少 2 个字)limit: 返回条数(可选,默认10,最大20)
- 说明:
- 当前版本先返回本系统历史单位开票记录中的企业名称建议,作为 fallback 模式
- 后续可在不改前端契约的前提下接入第三方企业信息服务商
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "companyId": "91330100MA12345678", "companyName": "杭州示例科技有限公司", "taxpayerNo": "91330100MA12345678", "displayText": "杭州示例科技有限公司 (91330100MA12345678)", "source": "local_history" } ] }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- 用户先完成登录,拿到
token - 团队 owner 创建团队:
POST /api/v1/teams - 团队 owner 添加成员:
POST /api/v1/teams/:teamId/members - 获取可购买团队套餐列表:
GET /api/v1/teamPlans - 查询可用支付渠道:
GET /api/v1/paymentChannels?itemType=team_plan&itemId=...&teamId=... - 用户选择
payChannel后创建订单:POST /api/v1/orders(itemType=team_plan,携带teamId;响应会直接包含payment支付入口信息) - (可选,兼容旧流程)统一发起支付:
POST /api/v1/orders/:orderNo/pay - 支付成功后:
- 服务端自动创建/续费订阅期并初始化席位
- 服务端自动为团队成员分配空闲席位(成员加入时优先自动入席;若成员先加入后购套餐/扩容,也会自动补齐入席)
- 管理员查询席位:
GET /api/v1/teams/:teamId/seats - (可选)管理员手动调整席位:
POST /api/v1/teams/:teamId/seats/:seatNo/assign/POST /api/v1/teams/:teamId/seats/:seatNo/unassign
- URL:
/api/v1/teams - Method:
GET - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "teamId": 1, "name": "Team A", "role": "OWNER", "ownerUserId": 123, "ownerUser": { "userId": 123, "nickname": "alice", "avatar": "https://cdn.xxx/a.png" } }, { "teamId": 2, "name": "Team B", "role": "MEMBER", "ownerUserId": 456, "ownerUser": { "userId": 456, "nickname": "bob", "avatar": "https://cdn.xxx/b.png" } } ] }
- Code:
- Error Response:
- Code:
401(未登录或 token 无效) - Code:
500(查询失败)
- Code:
- URL:
/api/v1/teams - Method:
POST - Headers:
Authorization: Bearer <token>
- Content-Type:
application/json - Request Body:
{ "name": "Team A" } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": { "id": 1, "name": "Team A", "ownerUserId": 123, "createdAt": "2026-03-12T08:00:00Z", "updatedAt": "2026-03-12T08:00:00Z", "deletedAt": null } }
- Code:
- Notes:
- 团队相关接口字段使用
camelCase(如ownerUserId/createdAt)。
- 团队相关接口字段使用
- Error Response:
- Code:
400(请求体不合法) - Code:
401(未登录或 token 无效) - Code:
500(创建失败)
- Code:
- URL:
/api/v1/teams/:teamId/members - Method:
GET - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "id": 1, "teamId": 1, "userId": 123, "user": { "userId": 123, "nickname": "alice", "avatar": "https://cdn.xxx/a.png" }, "role": "OWNER", "status": "ACTIVE", "joinedAt": "2026-03-12T08:00:00Z", "createdAt": "2026-03-12T08:00:00Z", "updatedAt": "2026-03-12T08:00:00Z" }, { "id": 2, "teamId": 1, "userId": 456, "user": { "userId": 456, "nickname": "bob", "avatar": "https://cdn.xxx/b.png" }, "role": "MEMBER", "status": "ACTIVE", "joinedAt": "2026-03-12T08:00:00Z", "createdAt": "2026-03-12T08:00:00Z", "updatedAt": "2026-03-12T08:00:00Z" } ] }
- Code:
- Error Response:
- Code:
400(teamId 不合法) - Code:
401(未登录或 token 无效) - Code:
500(查询失败/无权限)
- Code:
- URL:
/api/v1/teams/:teamId/members - Method:
POST - Headers:
Authorization: Bearer <token>
- Content-Type:
application/json - Request Body:
{ "userId": 456, "role": "MEMBER" }role可选:ADMIN/MEMBER(不传默认MEMBER)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success" }
- Code:
- Notes:
- 若团队存在“当前有效订阅期”且有空闲席位,服务端会在成员加入成功后自动分配席位(无需额外调用席位分配接口)
- 若团队存在“当前有效订阅期”但没有空闲席位,接口会返回错误(不允许“超席位”加入)
- 若团队当前没有有效订阅期(未购团队套餐),成员仍可加入;待后续购套餐/扩容后会自动补齐入席(按席位数量为止)
- Error Response:
- Code:
400(请求体不合法/teamId 不合法) - Code:
401(未登录或 token 无效) - Code:
500(添加失败/无权限/role 不合法)
- Code:
- URL:
/api/v1/teams/:teamId/members/:userId - Method:
DELETE - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success" }
- Code:
- Error Response:
- Code:
400(teamId/userId 不合法) - Code:
401(未登录或 token 无效) - Code:
500(移除失败/无权限/不能移除 owner)
- Code:
- URL:
/api/v1/teams/:teamId/seats - Method:
GET - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success", "data": [ { "seatNo": 1, "seatId": 101, "assignedUserId": 123, "assignmentId": 10001 }, { "seatNo": 2, "seatId": 102, "assignedUserId": null, "assignmentId": null } ] }
- Code:
- Error Response:
- Code:
400(teamId 不合法) - Code:
401(未登录或 token 无效) - Code:
500(查询失败/无权限/无有效订阅期)
- Code:
- URL:
/api/v1/teams/:teamId/seats/:seatNo/assign - Method:
POST - Headers:
Authorization: Bearer <token>
- Content-Type:
application/json - Request Body:
{ "userId": 456 } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success" }
- Code:
- Error Response:
- Code:
400(teamId/seatNo 不合法/请求体不合法) - Code:
401(未登录或 token 无效) - Code:
500(分配失败/无权限/用户非团队成员/无有效订阅期)
- Code:
- URL:
/api/v1/teams/:teamId/seats/:seatNo/unassign - Method:
POST - Headers:
Authorization: Bearer <token>
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "Success" }
- Code:
- Error Response:
- Code:
400(teamId/seatNo 不合法) - Code:
401(未登录或 token 无效) - Code:
500(解绑失败/无权限/无有效订阅期)
- Code:
- URL:
/health - Method:
GET - Success Response:
- Code:
200 - Body:
{ "code": 0, "msg": "OK", "data": { "status": "healthy" } }
- Code:
以下接口均需 Authorization: Bearer <token>(C 端用户 JWT)。上传接口成功后在 data 中返回 oss_id,提交反馈时在 JSON 中引用该字段。
- URL:
/api/v1/uploads/feedback/screenshot - Method:
POST - Content-Type:
multipart/form-data - Form 字段:
file:图片文件(image/jpeg、image/png、image/webp、image/gif),单文件最大 10MiB
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "oss_id": 10001 } }
- Code:
- Error Response(节选):
- Code:
400(error.feedback.file_required/error.feedback.file_too_large/error.feedback.file_type_not_allowed) - Code:
401(未登录或 token 无效) - Code:
500(OSS 或数据库失败)
- Code:
- URL:
/api/v1/uploads/feedback/log - Method:
POST - Content-Type:
multipart/form-data - Form 字段:
file:ZIP 压缩包,单文件最大 50MiB(服务端会校验为合法 zip)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "oss_id": 10002 } }
- Code:
- Error Response(节选): 同截图上传
- URL:
/api/v1/feedbacks - Method:
POST - Content-Type:
application/json - Request Body:
{ "content": "问题描述(必填,非空,最大约 2 万字符)", "contactInfo": "可选,手机/邮箱/微信等联系方式,最长 256 字符", "screenshotOssIds": [10001, 10003], "logOssId": 10002, "systemModel": "Windows 11 专业版", "cpuModel": "Intel Core i7-1360P", "gpuModel": "NVIDIA RTX 4060", "appVersion": "1.2.3", "platform": "win32", "clientId": "可选,客户端实例 ID" } - 字段说明:
contactInfo:可选,联系方式(字符串,最长 256 字符);未填或仅空白时存为空字符串。screenshotOssIds:可选,最多 3 个,须为先前截图上传返回的oss_id,且属于当前用户、用途为反馈截图;不可重复。logOssId:可选,须为先前日志上传返回的oss_id,且属于当前用户、用途为反馈日志。appVersion:必填(应用版本号);其余硬件与平台字段可选,由客户端自行采集。
- 企微通知:反馈落库成功后,若服务端
wecom.url已配置,将异步向企业微信群机器人推送文本(含反馈正文、用户有效渠道、appVersion作为 AIPC 版本);未配置 webhook 时静默跳过。 - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "id": 5001 } }
- Code:
- Error Response(节选):
- Code:
400(error.invalid_request_body、error.feedback.content_invalid、error.feedback.contact_invalid、error.feedback.screenshot_limit、error.feedback.duplicate_screenshot_oss、error.feedback.oss_invalid) - Code:
401(未登录或 token 无效) - Code:
500(服务器内部错误)
- Code:
以下接口均需 Authorization: Bearer <token>(仅 C 端用户 JWT;不支持 flowy- 用户 API 密钥)。数据来自论坛侧经主站投递接口写入的 tb_forum_inbox_messages;列表按 occurredAt 倒序。论坛服务端对接投递接口见 docs/forum-inbox-integration.md。
- URL:
/api/v1/forum/inbox/messages - Method:
GET - Query:
page:页码,默认1pageSize:每页条数,默认20,最大100onlyUnread:传true时仅返回未读(read_at为空)
- Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "list": [ { "id": 12345, "title": "标题", "body": "正文", "linkUrl": "https://forum.example.com/t/1", "occurredAt": "2026-05-06T12:00:00+08:00", "read": false, "readAt": null, "forumMessageId": "forum-msg-abc", "createdAt": "2026-05-06T12:00:01+08:00" } ], "total": 100, "page": 1, "pageSize": 20 } }
- Code:
- 字段说明(
list单项):read:true表示已读(read_at非空)。readAt:首次标记已读时间;未读为null。forumMessageId:若投递时带论坛幂等 ID 则有值,否则省略。
- Error Response(节选):
- Code:
401(未登录或 token 无效) - Code:
500(数据库错误)
- Code:
- URL:
/api/v1/forum/inbox/unreadCount - Method:
GET - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "unreadCount": 3 } }
- Code:
- Error Response(节选):
- Code:
401(未登录或 token 无效) - Code:
500(数据库错误)
- Code:
- URL:
/api/v1/forum/inbox/messages/:id/read - Method:
POST - Path:
id:站内信主键(数字)
- 说明:
- 仅允许操作当前登录用户名下的记录。
- 已读幂等:若该条已是已读,仍返回成功(
200,data省略)。
- Success Response:
- Code:
200 - Body: 与项目惯例一致,
code为200,无data字段(OKNoData)。
- Code:
- Error Response(节选):
- Code:
400(error.invalid_param,id非法) - Code:
401(未登录或 token 无效) - Code:
404(error.forum.message_not_found:不存在或不属于当前用户) - Code:
500(数据库错误)
- Code:
- URL:
/api/v1/forum/inbox/readAll - Method:
POST - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "updated": 5 } } updated:本次将read_at从空更新为非空的行数(若无未读则为0)。
- Code:
- Error Response(节选):
- Code:
401(未登录或 token 无效) - Code:
500(数据库错误)
- Code:
以下接口供 Flowy 论坛服务端 调用,不需要 C 端用户 JWT。用户以 email + 可选 channel 与主站 tb_users 对齐(规则同站内信投递,见 **docs/forum-inbox-integration.md §2)。
论坛侧在用户首次发帖时调用;主站按用户幂等发放积分,每个用户仅成功赠送一次(依赖 tb_credit_ledger 唯一约束 (user_id, reason, ref_type, ref_id))。
- URL:
/api/v1/integration/forum/first-post/reward - Method:
POST - Content-Type:
application/json - 鉴权: 无(不要求
Authorization) - 配置:
forum.first_post_reward_points(int64,为0时功能关闭,接口返回 503)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email |
string | 是 | 用户邮箱;主站会 trim 并转小写后匹配 |
channel |
string | 否 | 与主站用户 channel 一致;省略时使用 forum.default_user_channel(默认 flowy) |
{
"email": "user@example.com",
"channel": "flowy"
}{
"code": 200,
"msg": "操作成功",
"data": {
"grantedPoints": 200,
"balance": 520,
"duplicate": false
}
}| 字段 | 说明 |
|---|---|
grantedPoints |
本次请求实际发放的积分;若用户已领取过则为 0 |
balance |
发放后用户当前可用积分余额(按批次快照计算) |
duplicate |
true 表示该用户此前已领取首次发帖奖励,本次未重复发放 |
| HTTP | errorKey | 说明 |
|---|---|---|
| 400 | error.invalid_request_body |
JSON 无法绑定(如 email 缺失或格式非法) |
| 404 | error.forum.user_not_found |
该邮箱 + 渠道下无主站用户 |
| 503 | error.forum.first_post_reward_disabled |
forum.first_post_reward_points 未配置或为 0 |
| 500 | error.internal |
服务器内部错误 |
curl -sS -X POST 'https://<API_HOST>/api/v1/integration/forum/first-post/reward' \
-H 'Content-Type: application/json' \
-d '{
"email": "user@example.com",
"channel": "flowy"
}'- 论坛侧应在确认「首次发帖」后再调用;主站以用户维度去重,重复调用安全。
- 并发重复请求时,仅一笔积分入账成功,其余返回
duplicate: true、grantedPoints: 0。 - 积分流水
reason为forum_first_post,用户积分明细中展示为「论坛首次发帖奖励」。
以下接口均需使用后台会话访问,对应路由前缀为 /api/v1/sys。
- URL:
/api/v1/sys/stats/dashboard - Method:
GET - 说明:
- 返回运营首页所需的聚合统计
channelAnalytics中与支付金额相关的字段已区分“净额”和“毛额”- 净额口径会排除已成功全额退款的订单;毛额口径保留原始支付金额
- 关键字段说明:
channelAnalytics.totalPaidAmountCent:全渠道净支付金额(分)channelAnalytics.totalPaidAmountGrossCent:全渠道毛支付金额(分)channelAnalytics.items[].paidAmountCent:该渠道净支付金额(分)channelAnalytics.items[].paidAmountGrossCent:该渠道毛支付金额(分)channelAnalytics.items[].paidAmountShare:该渠道净支付金额占比channelAnalytics.items[].paidAmountGrossShare:该渠道毛支付金额占比clientAppUserCounts.aipc:曾通过 AIPC 客户端登录的用户数(tb_users.app_aipc = 1)clientAppUserCounts.herdsman:曾通过 Herdsman 客户端登录的用户数(tb_users.app_herdsman = 1)clientAppUserCounts.flowymes:曾通过 FlowyMes 客户端登录的用户数(tb_users.app_flowymes = 1)userAnalytics.trend[].newAipcUsers:当日首次通过 AIPC 客户端登录的新用户数(app_aipc_at落在当日;登录未传app时默认 AIPC)userAnalytics.trend[].newHerdsmanUsers:当日首次通过 Herdsman 客户端登录的新用户数(app_herdsman_at落在当日)userAnalytics.trend[].newFlowyMesUsers:当日首次通过 FlowyMes 客户端登录的新用户数(app_flowymes_at落在当日;与clientAppUserCounts.flowymes标记口径一致)clientAppDeviceCounts.aipc:由 AIPC 客户端上报的成功激活设备数(tb_activate_device.client_app = 'aipc')clientAppDeviceCounts.herdsman:由 Herdsman 客户端上报的成功激活设备数(tb_activate_device.client_app = 'herdsman')clientAppDeviceCounts.flowymes:由 FlowyMes 客户端上报的成功激活设备数(tb_activate_device.client_app = 'flowymes')
- URL:
/api/v1/sys/orders/repurchase - Method:
GET - Query Params:
page:页码(默认1)pageSize:每页条数(默认20,最大100)keyword:按邮箱 / 昵称模糊搜索(可选)
- 说明:
- 复购用户定义为“有效支付订单数 >= 2”的 C 端用户
- 有效支付订单口径会排除:
- 已成功全额退款的订单
- 当日支付后又全额退款的订单
- URL:
/api/v1/sys/orders/repurchase/export - Method:
GET - Query Params:
keyword:按邮箱 / 昵称模糊搜索(可选)
- 说明:
- 导出范围与复购用户列表保持同一统计口径
- 导出的订单明细仅包含“有效支付订单”,不会包含当日支付后又全额退款的订单
以下接口均需请求头 Authorization: Bearer <token>,与现有 C 端接口一致。模型调用与 /v1/chat/completions 共用 model_proxy,meeting_summary.model 应填写与对话接口 model 字段相同的字符串(例如 Pro/MiniMaxAI/MiniMax-M2.5 走自动选路;若需按库表 tb_model.name 精确指定渠道,可写 flowy/<name>)。积分扣减与 tb_user_chat 与对话一致。需 meeting_summary.enabled=true 且能解析出模型名。
语言 language:传 zh 或 en(也兼容 zh-CN、en-US 等),用于选择内置中/英提示词。
一次请求 并行 三次非流式 LLM:分别用 meeting_summary.topic、meeting_summary.keywords 与 meeting_summary.todos 的 temperature / max_tokens 生成「会议主题」「会议关键词」与「待办事项列表」(待办提示词区分中/英,与内置 meetingllm/prompts/todos.*.txt 一致)。积分与 tb_user_chat 记 三次 调用;运营审计仍一条 call_type=topic_keywords(messages_json 内含三步:topic、keywords、todos)。需配置可解析的 meeting_summary.model(或旧版子任务 model),否则返回 503(error.meeting_summary.not_configured)。
超时与网关:受 meeting_summary.http_timeout 限制;若前置 Nginx/API 网关 proxy_read_timeout(或等价)过短,客户端可能 504,需调大。
- URL:
/api/v1/meeting/topicKeywords - Method:
POST - Content-Type:
application/json - Request Body:
{ "asrText": "会议 ASR 转写全文", "userNotes": "用户笔记,可为空(仅用于主题子任务)", "language": "zh" } - Success Response:
- Code:
200 - Body:
{ "code": 200, "msg": "操作成功", "data": { "topic": "不超过约二十字的会议主题纯文本", "keywords": ["关键词1", "关键词2"], "todoItems": [ { "type": "todo", "title": "任务或行动描述", "assignee": "负责人,可为空", "reminderTime": "截止时间,可为空(建议 ISO 8601)", "priority": "high" } ] } } todoItems:type为todo(一次性待办)或recurring(周期性提醒);priority为high/medium/low之一或省略;无待办时为空数组[]。
- Code:
- Error Response(节选):
- Code:
400(error.meeting_summary.disabled、error.invalid_param(language/asrText)、error.meeting_summary.asr_too_long、error.meeting_summary.user_notes_too_long) - Code:
402(error.insufficient_credit,与对话接口一致) - Code:
429(error.rate_limited) - Code:
503(error.meeting_summary.not_configured:未配置模型名等) - Code:
502(error.meeting_summary.topic_parse_failed/error.meeting_summary.keywords_parse_failed/error.meeting_summary.todos_parse_failed:对应子任务模型输出无法解析时) - Code:
500(error.internal、error.all_channel_models_failed)
- Code:
- URL:
/api/v1/meeting/minutes/stream - Method:
POST - Content-Type:
application/json - Accept:
text/event-stream(建议) - Request Body:
{ "participants": "参会人员", "meetingTime": "会议时间描述", "asrText": "会议 ASR 转写全文", "userNotes": "用户手写笔记,可为空", "language": "zh" } - Success Response:
- Code:
200 - Content-Type:
text/event-stream - Body: OpenAI 兼容 SSE(
data: {...}行),与标准流式补全一致;客户端请使用fetch+ReadableStream解析(勿用仅支持 GET 的EventSource携带大 Body)。
- Code:
- Error Response:
- 若在建立流之前校验失败或未开始写入 SSE,返回 JSON 错误(同上,含
error.meeting_summary.participants_too_long、error.meeting_summary.meeting_time_too_long等)。 - 若已开始推送 SSE 后中断,连接关闭,无二次 JSON 包体。
- 若在建立流之前校验失败或未开始写入 SSE,返回 JSON 错误(同上,含