openapi: 3.1.0 info: title: SolveHub API version: 1.3.1 description: '预付费验证码任务 API。金额均为 USD 微美元整数(1000000 = $1)。错误响应统一为 {"error":"CODE"},响应头 X-Request-ID 为请求编号。数据库瞬时故障(节点切换、锁冲突、连接中断)由服务端自动重试;重试约 10 秒仍未成功时返回 503 DATABASE_BUSY 并带 Retry-After: 1,此时没有半完成的写入,但若提交结果无法确认请求可能已生效:写请求应带幂等键原样重试或先查询资源。客户接口可用 Authorization: Bearer ,或客户控制台会话 Cookie(写请求须带 X-SolveHub-Portal: 1)。管理接口 /v1/admin/* 只在管理端口提供。' servers: - url: https://www.tiam.codes description: 生产 - url: http://127.0.0.1:8080 description: 本地 security: - bearerAuth: [] paths: /v1/datadome/build-url: post: tags: [Tools] summary: 从捕获的 DataDome 拦截响应构建挑战 URL(免费同步工具) description: '要求 tasks 权限和可用账户;不创建任务、不预占余额、不计费。至少提供 body、location、captchaUrl 之一;已提供的 captchaUrl 优先。HTML/JSON 只提交解析,不会由平台请求 websiteURL 或执行其中脚本。结果 URL 原样用于 datadome 解题任务。繁忙时返回 503 和 Retry-After。' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/DataDomeURLRequest' } responses: '200': description: 已构建 URL,无需轮询 content: application/json: schema: { $ref: '#/components/schemas/DataDomeURLResult' } '400': { description: INVALID_DATADOME_PAYLOAD,参数无效、超长或格式错误 } '401': { description: UNAUTHORIZED 或 API_KEY_EXPIRED } '403': { description: 权限、来源 IP 或账户状态不允许调用 } '422': { description: DATADOME_CHALLENGE_NOT_FOUND,未识别出有效挑战 } '502': { description: DATADOME_BUILDER_FAILED,解析服务响应异常 } '503': { description: DATADOME_BUILDER_UNAVAILABLE 或数据库繁忙,退避后重试 } /buildDataDomeUrl: post: tags: [Compatibility] summary: 免费 DataDome URL 构建工具兼容接口 security: [] description: clientKey 是 SolveHub API Key,要求 tasks 权限和可用账户。无论成功或业务失败都返回 HTTP 200,检查 errorId;不创建任务、不计费。 requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [clientKey, websiteURL] anyOf: - required: [body] - required: [location] - required: [captchaUrl] properties: clientKey: { type: string, description: SolveHub API Key } websiteURL: { $ref: '#/components/schemas/DataDomeURLRequest/properties/websiteURL' } body: { $ref: '#/components/schemas/DataDomeURLRequest/properties/body' } location: { $ref: '#/components/schemas/DataDomeURLRequest/properties/location' } captchaUrl: { $ref: '#/components/schemas/DataDomeURLRequest/properties/captchaUrl' } responses: '200': description: errorId 为 0 表示成功;失败时包含 errorCode 和 errorDescription content: application/json: schema: type: object required: [errorId] properties: errorId: { type: integer, enum: [0, 1] } captchaUrl: { type: string, format: uri } challengeType: { type: string, enum: [captcha, interstitial] } errorCode: { type: [string, 'null'] } errorDescription: { type: [string, 'null'] } /v1/cregis/config: get: tags: [Billing] summary: 查询 USDT 自助充值开关、动态币种列表与网络;未绑定账户也可访问 responses: '200': { description: 'enabled、ready、coins(name/chainId/tokenId/decimals)、minimumAmount(字符串 0.50)、sharedAddress' } /v1/cregis/activate: post: tags: [Billing] summary: 已验证邮箱的账户会话开通自助充值,保留原客户和钱包并创建加密首把 Key security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } responses: '204': { description: 已开通;重复请求不会重复创建 Key } '409': { description: 需先验证邮箱或账户状态不允许 } '503': { description: 充值未开放 } /v1/cregis/orders: get: tags: [Billing] summary: 查询当前客户最近 50 条 USDT 充值订单 responses: '200': { description: 'orders 数组;包含 orderId、network、address、requestedAmount、payAmount、status、createdAt、expiresAt;金额为精确十进制字符串' } '503': { description: 充值未开放 } post: tags: [Billing] summary: 创建 USDT 充值订单,必须先确认共享地址规则 description: 固定地址池不会按客户生成新地址。必须按本单网络、地址和精确 payAmount 一次性付款,不得直接复用历史地址;过期、错额、重复付款及无订单入金需提交账务工单。最低 0.50 USD,无业务上限,保留整数存储边界。应付金额等于输入金额,不添加金额尾数;同地址同金额有多个历史候选订单时转人工核对。实际到账按 1 USDT 记 1 USD 入余额,手续费另计。 parameters: - in: header name: Idempotency-Key required: true schema: { type: string, minLength: 8, maxLength: 128 } requestBody: required: true content: application/json: schema: type: object required: [chainId, tokenId, amount, sharedAddressAcknowledged] properties: chainId: { type: string, description: Cregis 返回的 chain_id } tokenId: { type: string, description: Cregis 返回的 token_id(代币为合约地址) } amount: { type: string, description: 至少 0.50,最多两位小数 } sharedAddressAcknowledged: { type: boolean, const: true } responses: '201': { description: 'orderId、coinName、address、requestedAmount、payAmount、status、createdAt、expiresAt;金额为字符串' } '400': { description: 参数错误或未确认提示 } '409': { description: 已有待付款订单、分配容量不足或幂等键冲突 } '503': { description: 充值未开放 } /v1/cregis/orders/{id}/cancel: post: tags: [Billing] summary: 取消仍在付款窗口内的待付款充值订单;金额尾数保留不回收 parameters: - in: path name: id required: true schema: { type: string } responses: '200': { description: 'status 变为 cancelled' } '404': { description: 订单不存在(ORDER_NOT_FOUND) } '409': { description: 已过期或已处理,无法取消(ORDER_NOT_CANCELLABLE) } '503': { description: 充值未开放 } /v1/cregis/deposit: post: tags: [Billing] summary: Cregis WaaS 入金回调,必须校验项目签名;不是客户充值申请接口 security: [] requestBody: required: true content: application/json: schema: type: object description: Cregis WaaS depositCallback 原始签名字段,包括 pid、cid、chain_id、token_id、currency、address、amount、status、txid、nonce、timestamp、sign、block_time;请求体不超过 64 KiB responses: '200': description: 入账或异常待核对记录已可靠提交;重复回调不重复入账 content: text/plain: schema: { type: string, const: success } '400': { description: 回调格式或签名无效 } '500': { description: 数据库或入金身份冲突;不确认回调成功 } '503': { description: 充值未开放 } /v1/announcements: get: tags: [Announcements] summary: 网站公告列表;只返回已发布且启用网站渠道的纯文本公告 security: [] parameters: - in: query name: limit schema: { type: integer, minimum: 1, maximum: 100, default: 50 } - in: query name: offset schema: { type: integer, minimum: 0, maximum: 100000, default: 0 } responses: '200': { description: announcements 与 total;只包含 id、title、body、publishedAt } /v1/portal/register/email: post: tags: [Portal] summary: 注册第一步——向未注册邮箱发送一次性验证码(20 分钟有效) description: 注册必须先通过邮箱验证,因此开户前调用本接口获取验证码。重复请求会让旧验证码立即失效;验证码只保存摘要,错误累计 5 次即作废。按来源 IP 计数,15 分钟内超过 20 次返回 429 REGISTER_THROTTLED(带 Retry-After)。 security: [] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [email] properties: email: { type: string, format: email } responses: '204': { description: 验证码已排入发件箱 } '400': { description: 邮箱格式无效(INVALID_ACCOUNT) } '409': { description: 邮箱已注册(ACCOUNT_EXISTS) } '429': { description: 请求过于频繁(REGISTER_THROTTLED) } /v1/portal/register: post: tags: [Portal] summary: 注册第二步——凭邮箱验证码创建账户;注册后为未绑定状态(unbound),须登录后绑定 API Key 才能使用业务接口 description: 必须携带 POST /v1/portal/register/email 下发的 code,校验通过即标记登录邮箱已验证,因此不会产生邮箱未验证的账户;验证码一次性消费,错误、过期或超过次数返回 400 EMAIL_CODE_INVALID。不创建 API Key。按来源 IP 计数,15 分钟内超过 20 次返回 429 REGISTER_THROTTLED(带 Retry-After)。未绑定账户只能访问会话、账户安全设置与 POST /v1/portal/account/bind-key,其余客户接口返回 403 ACCOUNT_UNBOUND。 security: [] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [email, password, code] properties: email: { type: string, format: email } password: { type: string, minLength: 12 } code: { type: string, description: 注册验证码(来自 /v1/portal/register/email) } responses: '201': { description: 'customerId 与 status: unbound' } '400': { description: 邮箱或密码不符合要求(INVALID_ACCOUNT),或验证码无效(EMAIL_CODE_INVALID) } '409': { description: 邮箱已注册(ACCOUNT_EXISTS) } '429': { description: 注册过于频繁(REGISTER_THROTTLED) } /v1/portal/account/bind-key: post: tags: [Portal] summary: 账户会话粘贴 API Key 完成绑定,或为本客户的旧 Key 启用再次查看 description: 未绑定账户:账户(凭据、会话、恢复码)整体并入该 Key 所属客户,当前会话随之归属该客户,无需重新登录;该客户已绑定其他账户时返回 409 KEY_ALREADY_BOUND。已绑定账户:只能粘贴本客户的 Key,服务端加密保存其明文以便再次查看;粘贴其他客户的 Key 返回 409 KEY_OWNED_BY_OTHER_CUSTOMER。无效、吊销、已被绑定或属于其他客户的尝试按来源 IP 计数,15 分钟内超过 20 次返回 429 BIND_THROTTLED。 security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [apiKey] properties: apiKey: { type: string } responses: '200': { description: customerId、keyId;merged 为 true 表示未绑定账户已并入该客户 } '403': { description: 非账户会话(ACCOUNT_SESSION_REQUIRED)或 Key 所属客户已关闭(ACCOUNT_CLOSED) } '404': { description: Key 无效或已吊销(KEY_NOT_FOUND) } '409': { description: KEY_ALREADY_BOUND、KEY_OWNED_BY_OTHER_CUSTOMER 或 UNBOUND_NOT_EMPTY } '429': { description: 失败次数过多(BIND_THROTTLED) } '503': { description: 服务端未配置数据加密密钥,无法启用查看(KEY_VIEW_UNAVAILABLE) } /v1/portal/account/telegram: get: tags: [Portal] summary: 查询账户的 Telegram 绑定与试用余额状态 description: 新用户绑定并验证 Telegram 后一次性获得 0.1 USD 试用余额,余额永不过期;试用账户最多同时运行 1 个任务(customer_limits.max_active = 1)。enabled 为 false 表示服务端未启用该 Bot,绑定入口不可用。绑定不可解绑。 security: [{ portalSession: [] }] responses: '200': { description: enabled、status(空表示未发起、pending、verified)、tgUsername、verifiedAt、trialCredited、trialAmountMicros、trialMaxActive } '403': { description: 非账户会话(ACCOUNT_SESSION_REQUIRED) } /v1/portal/account/telegram/bind: post: tags: [Portal] summary: 生成一次性 Telegram 深链(有效期 15 分钟),在 Bot 内点击后自动入账 0.1 USD description: 返回 t.me 深链;令牌由 CSPRNG 生成且只保存摘要,每次调用都会让上一条链接立即失效。Bot 收到 /start 后按 Telegram 数字 user_id 去重:同一 Telegram 账号只能绑定一个账户,重复绑定返回 TELEGRAM_ALREADY_BOUND。验证成功后账户自动从 unbound 转为 active、钱包入账 0.1 USD,并把客户并发上限收紧为 1。请求体可选;传入 locale(zh/en/ja)时 Bot 用该语言回复,否则退回 Telegram 客户端界面语言。 security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: false content: application/json: schema: type: object properties: locale: { type: string, description: 控制台界面语言,Bot 回复语言优先使用它 } responses: '200': { description: botUsername、deepLink 与 expiresAt } '403': { description: 非账户会话(ACCOUNT_SESSION_REQUIRED)或账户已关闭(ACCOUNT_CLOSED) } '409': { description: 已绑定并领取过试用余额,不支持解绑(TELEGRAM_ALREADY_CLAIMED) } '503': { description: 未配置 Bot(TELEGRAM_UNAVAILABLE) } /v1/portal/session/account: post: tags: [Portal] summary: 先验证邮箱与密码;若已开启 TOTP 返回 TOTP_REQUIRED,再提交动态验证码或一次性恢复码登录 security: [] requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: email: { type: string, format: email } password: { type: string } code: { type: string, description: 2FA 已开启时必填的 6 位动态验证码或一次性恢复码 } responses: '200': { description: 签发独立于 API Key 的 HttpOnly 账户会话 } '401': { $ref: '#/components/responses/Error' } '429': { $ref: '#/components/responses/Error' } /v1/portal/account/security: get: tags: [Portal] summary: 查看当前客户是否已开通账户登录及 2FA 状态 security: [{ portalSession: [] }, { bearerAuth: [] }] responses: '200': { description: registered、totpEnabled;账户会话另返回 email、emailVerified } /v1/portal/account/email-verification: post: tags: [Portal] summary: 向当前账户的登录邮箱发送一次性验证邮件 description: 仅接受账户会话;SMTP 未配置返回 503。每客户至少间隔 15 分钟。 security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } responses: '204': { description: 已排入邮件队列;验证码 20 分钟内有效 } '403': { $ref: '#/components/responses/Error' } '429': { $ref: '#/components/responses/Error' } '503': { $ref: '#/components/responses/Error' } /v1/portal/account/email-verification/confirm: post: tags: [Portal] summary: 验证登录邮箱 security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [code] properties: code: { type: string, description: 邮件中的一次性验证码 } responses: '204': { description: 登录邮箱已验证 } '400': { $ref: '#/components/responses/Error' } /v1/portal/contact-email/verification: post: tags: [Portal] summary: 向当前通知邮箱发送一次性验证码 description: 仅接受密码账户会话;通知邮箱与登录邮箱各自验证。每客户至少间隔 15 分钟。 security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } responses: '204': { description: 已排入邮件队列;验证码 20 分钟内有效 } '400': { $ref: '#/components/responses/Error' } '403': { $ref: '#/components/responses/Error' } '409': { $ref: '#/components/responses/Error' } '429': { $ref: '#/components/responses/Error' } '503': { $ref: '#/components/responses/Error' } /v1/portal/contact-email/verification/confirm: post: tags: [Portal] summary: 验证当前通知邮箱 description: 修改通知邮箱后旧验证码立即失效;同一验证码只能使用一次。 security: [{ portalSession: [] }] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [code] properties: code: { type: string, description: 邮件中的一次性验证码 } responses: '204': { description: 通知邮箱已验证 } '400': { $ref: '#/components/responses/Error' } '403': { $ref: '#/components/responses/Error' } /v1/portal/account/password-recovery: post: tags: [Portal] summary: 请求密码恢复邮件 description: 无需登录;未知或未验证邮箱也返回 204,避免枚举账户。通知邮箱不能用于恢复。 security: [] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [email] properties: email: { type: string, format: email } responses: '204': { description: 已受理;符合条件的邮箱将收到验证码 } '429': { $ref: '#/components/responses/Error' } '503': { $ref: '#/components/responses/Error' } /v1/portal/account/password-recovery/confirm: post: tags: [Portal] summary: 用已验证邮箱的一次性验证码恢复密码 description: 已启用 2FA 时仍须提交当前动态验证码或一次性恢复码;成功后撤销全部账户会话。 security: [] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object required: [email, token, password] properties: email: { type: string, format: email } token: { type: string, description: 邮件中的一次性验证码 } password: { type: string, minLength: 12, maxLength: 128 } code: { type: string, description: 已启用 2FA 时必填的 6 位动态验证码或一次性恢复码 } responses: '204': { description: 密码已更新,账户会话已撤销 } '400': { $ref: '#/components/responses/Error' } '429': { $ref: '#/components/responses/Error' } /v1/portal/account/claim: post: tags: [Portal] summary: 持有 account 权限 API Key 的客户在 Key 会话中自行绑定邮箱和密码,每个客户只能绑定一次 security: [{ portalSession: [] }] requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: email: { type: string, format: email } password: { type: string, minLength: 12 } responses: '204': { description: 已绑定;原有余额及全部 API Key 保持归属同一客户 } '400': { $ref: '#/components/responses/Error' } '403': { description: 非 Key 会话(API_KEY_SESSION_REQUIRED)、Key 缺少 account 权限(INSUFFICIENT_SCOPE)或客户已关闭(ACCOUNT_CLOSED) } '409': { description: 当前客户已绑定过邮箱或该邮箱已被其他客户使用(ACCOUNT_EXISTS) } /v1/portal/keys/{id}/compromise: post: tags: [Portal] summary: 已验证的账户会话应急吊销疑似泄露的 Key,允许最后一把 Key,并撤销其控制台会话 security: [{ portalSession: [] }] parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '204': { description: 已吊销 } '403': { $ref: '#/components/responses/Error' } '404': { $ref: '#/components/responses/Error' } /v1/tickets: get: tags: [Tickets] summary: 查询当前客户工单,支持 status、limit、offset security: [{ portalSession: [] }, { bearerAuth: [] }] responses: '200': { description: tickets 数组与 total 总数 } post: tags: [Tickets] summary: 创建工单;每客户最多 20 个未关闭工单、24 小时内最多 10 个 description: 提供 Idempotency-Key 时,24 小时内同一客户以同一键和相同请求重试不会新建工单,返回 200 与原工单当前状态(响应头 Idempotent-Replayed 为 true,即使工单此后已关闭也照常重放);请求内容不同或该键已用于回复时返回 409 IDEMPOTENCY_CONFLICT。不提供该头时每次请求都会新建工单。 security: [{ portalSession: [] }, { bearerAuth: [] }] parameters: - $ref: '#/components/parameters/TicketIdempotencyKey' requestBody: required: true content: application/json: schema: type: object required: [category, subject, body] properties: category: { type: string, enum: [technical, billing, account, other] } subject: { type: string, minLength: 5, maxLength: 120 } body: { type: string, minLength: 10, maxLength: 5000 } responses: '201': { description: 创建的 ticket } '200': description: 幂等重放,返回原 ticket 当前状态,未新建工单 headers: Idempotent-Replayed: description: 幂等重放时为 true schema: { type: string, enum: ['true'] } '400': description: INVALID_IDEMPOTENCY_KEY、INVALID_TICKET 或 INVALID_JSON $ref: '#/components/responses/Error' '409': description: IDEMPOTENCY_CONFLICT(同一幂等键对应不同请求) $ref: '#/components/responses/Error' '429': description: TICKET_LIMIT_REACHED 或 TICKET_RATE_LIMITED $ref: '#/components/responses/Error' /v1/tickets/{id}: get: tags: [Tickets] summary: 获取当前客户工单和最近 50 条公开消息;before 用于加载更早消息 security: [{ portalSession: [] }, { bearerAuth: [] }] parameters: - in: path name: id required: true schema: { type: string, format: uuid } - in: query name: before schema: { type: integer, format: int64, minimum: 1 } responses: '200': { description: ticket、messages、nextBefore } '404': { $ref: '#/components/responses/Error' } /v1/tickets/{id}/messages: post: tags: [Tickets] summary: 客户回复未关闭工单 description: 提供 Idempotency-Key 时,24 小时内同一客户对同一工单以同一键和相同正文重试不会追加消息,返回 200 与工单当前状态(响应头 Idempotent-Replayed 为 true,即使工单此后已关闭也照常重放);正文不同、工单不同或该键已用于创建工单时返回 409 IDEMPOTENCY_CONFLICT。 security: [{ portalSession: [] }, { bearerAuth: [] }] parameters: - in: path name: id required: true schema: { type: string, format: uuid } - $ref: '#/components/parameters/TicketIdempotencyKey' requestBody: required: true content: application/json: schema: type: object required: [body] properties: body: { type: string, minLength: 1, maxLength: 5000 } responses: '200': description: 更新后的 ticket;幂等重放时未追加消息 headers: Idempotent-Replayed: description: 幂等重放时为 true schema: { type: string, enum: ['true'] } '400': description: INVALID_IDEMPOTENCY_KEY、INVALID_TICKET 或 INVALID_JSON $ref: '#/components/responses/Error' '404': { $ref: '#/components/responses/Error' } '409': description: TICKET_CLOSED 或 IDEMPOTENCY_CONFLICT $ref: '#/components/responses/Error' /v1/tickets/{id}/close: post: tags: [Tickets] summary: 客户关闭工单 security: [{ portalSession: [] }, { bearerAuth: [] }] parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': { description: 更新后的 ticket } /v1/tickets/{id}/reopen: post: tags: [Tickets] summary: 客户重新打开已关闭工单 security: [{ portalSession: [] }, { bearerAuth: [] }] parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': { description: 更新后的 ticket } /v1/portal/account/totp: post: tags: [Portal] summary: 验证账户密码后生成待确认的 TOTP 密钥 security: [{ portalSession: [] }] responses: '200': { description: secret 和 otpauth URI,仅本次显示 } /v1/portal/account/totp/confirm: post: tags: [Portal] summary: 输入动态验证码确认开启 2FA security: [{ portalSession: [] }] responses: '200': { description: 已开启,返回只显示一次的 recoveryCodes 数组,含 8 个一次性恢复码 } /v1/portal/account/totp/recovery-codes: post: tags: [Portal] summary: 验证账户密码和新的动态验证码后重新生成 8 个恢复码,旧码立即失效 security: [{ portalSession: [] }] responses: '200': { description: 返回只显示一次的 recoveryCodes 数组 } /v1/portal/account/totp/disable: post: tags: [Portal] summary: 用当前密码和动态验证码或一次性恢复码关闭 2FA,清除所有恢复码 security: [{ portalSession: [] }] responses: '204': { description: 已关闭 } /v1/portal/account/password: post: tags: [Portal] summary: 修改账户密码并撤销全部账户会话;2FA 已开启时还需动态验证码 security: [{ portalSession: [] }] responses: '204': { description: 已修改 } /v1/recharge-codes/redeem: post: tags: [Billing] summary: 兑换管理员发行的一次性充值 Key,原子更新钱包和账本 security: [{ portalSession: [] }, { bearerAuth: [] }] requestBody: required: true content: application/json: schema: type: object required: [code] properties: code: { type: string } responses: '200': { description: 返回 amountMicros } '409': { $ref: '#/components/responses/Error' } /healthz: get: tags: - System security: [] responses: '200': description: 进程运行中 summary: 进程存活检查 /readyz: get: tags: - System security: [] responses: '200': description: 数据库可访问 '503': description: 数据库不可访问 summary: 数据库就绪检查 /v1/prices: get: tags: - Tasks security: [] summary: 查询任务目录与价格 responses: '200': description: 各任务类型的当前价格、可用状态与版本;来源为共享数据库 content: application/json: schema: type: object additionalProperties: $ref: '#/components/schemas/Price' /v1/me/prices: get: tags: - Tasks summary: 查询本客户的生效单价(含协议价) description: 任意有效 API Key(不限权限,过期 Key 亦可)或控制台会话均可读取,只返回调用者自己的报价。协议价不低于当前采购价时生效(negotiated 为 true),否则回退公开标价;停用或未配置的类型不报价。报价规则与建单一致,但最终预占金额以创建响应的 priceMicros 为准。采购价永不返回。 responses: '200': description: 按任务类型命名的生效单价 content: application/json: schema: type: object additionalProperties: $ref: '#/components/schemas/QuotedPrice' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' /v1/tasks: post: tags: - Tasks summary: 创建异步任务并预占余额 description: 24 小时内以同一幂等键和相同请求体重试时不会再次预占余额,直接返回原任务的当前状态,内容与 GET /v1/tasks/{id} 一致(已完成任务包含 solution,失败或待核对任务包含 errorCode);请求体不同返回 409 IDEMPOTENCY_CONFLICT。 parameters: - in: header name: Idempotency-Key required: true description: 幂等键;网络重试时复用同一个值和完全相同的请求体 schema: type: string minLength: 8 maxLength: 128 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateTask' responses: '202': description: 已创建任务,或幂等重放返回的原任务当前状态 content: application/json: schema: $ref: '#/components/schemas/Task' '400': $ref: '#/components/responses/Error' '402': $ref: '#/components/responses/Error' '403': description: KEY_SPEND_FROZEN、KEY_QUOTA_EXHAUSTED(Key 总额度已用尽)或账户权限限制 $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' '429': description: TASK_LIMIT_EXCEEDED、KEY_SPEND_LIMIT 或 KEY_CONCURRENCY_LIMIT(该 Key 的并发任务数已达上限);均带 Retry-After $ref: '#/components/responses/Error' get: tags: - Tasks summary: 按创建时间倒序列出任务(键集分页) parameters: - in: query name: keyId description: 仅统计或查询当前客户此 API Key 的任务;省略时包含所有 Key 和历史未记录 Key 的任务 schema: type: string maxLength: 64 - in: query name: limit schema: type: integer minimum: 1 maximum: 100 default: 50 - in: query name: status schema: type: string enum: - queued - dispatching - processing - uncertain - completed - failed - cancelled - in: query name: type schema: type: string description: 任务类型 - in: query name: from schema: type: string format: date-time description: RFC3339,含 - in: query name: to schema: type: string format: date-time description: RFC3339,不含 - in: query name: cursor schema: type: string description: 上一页返回的 nextCursor - in: query name: offset schema: type: integer minimum: 0 maximum: 100000 description: 按页码分页时跳过的条数;不能与 cursor 同时使用。带此参数时响应包含 total responses: '200': description: 任务列表 content: application/json: schema: type: object properties: tasks: type: array items: $ref: '#/components/schemas/Task' nextCursor: type: string description: 为空表示没有下一页 total: type: integer format: int64 description: 仅在请求带 offset 时返回,符合筛选条件的任务总数 '400': description: INVALID_LIMIT、INVALID_STATUS、INVALID_TYPE、INVALID_TIME_RANGE、INVALID_CURSOR 或 INVALID_OFFSET $ref: '#/components/responses/Error' /v1/tasks/{id}: get: tags: - Tasks summary: 查询任务状态与结果 parameters: - $ref: '#/components/parameters/TaskID' responses: '200': description: 任务详情 content: application/json: schema: $ref: '#/components/schemas/Task' '404': $ref: '#/components/responses/Error' /v1/tasks/{id}/events: get: tags: - Tasks summary: 查询任务状态事件 parameters: - $ref: '#/components/parameters/TaskID' responses: '200': description: 状态事件 content: application/json: schema: type: object properties: events: type: array items: $ref: '#/components/schemas/Event' '404': $ref: '#/components/responses/Error' /v1/balance: get: tags: - Billing summary: 查询余额 responses: '200': description: 客户钱包 content: application/json: schema: $ref: '#/components/schemas/Balance' /v1/ledger: get: tags: - Billing summary: 查询账本流水 parameters: - in: query name: kind description: 在全部历史流水中按类型筛选;后续分页需保留此参数 schema: type: string enum: [credit, reserve, capture, refund, fee, reversal, adjustment] - in: query name: before schema: type: integer format: int64 minimum: 1 responses: '200': description: 账本分页 content: application/json: schema: type: object properties: entries: type: array items: $ref: '#/components/schemas/LedgerEntry' nextCursor: type: string /v1/me: get: summary: 查询当前客户 security: - bearerAuth: [] responses: '200': description: 客户资料 content: application/json: schema: $ref: '#/components/schemas/MeResponse' '401': $ref: '#/components/responses/Error' put: summary: 修改当前客户的通知邮箱(需要邮箱密码账户会话) description: 地址发生变更时立即清除通知邮箱验证状态;在新地址验证前不发送业务邮件。 security: - portalSession: [] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [contactEmail] properties: contactEmail: { type: string, format: email } responses: '200': description: 更新后的客户资料 content: application/json: schema: { $ref: '#/components/schemas/Account' } '400': { $ref: '#/components/responses/Error' } '401': { $ref: '#/components/responses/Error' } '403': { $ref: '#/components/responses/Error' } /v1/keys: get: summary: 列出当前客户的 API Key 元数据 security: - bearerAuth: [] responses: '200': description: 密钥元数据 content: application/json: schema: type: object properties: keys: type: array items: $ref: '#/components/schemas/APIKeyMeta' '401': $ref: '#/components/responses/Error' post: summary: 创建 API Key(仅邮箱密码账户会话),明文加密保存,可经 reveal 再次查看;最多 20 把有效密钥 security: - portalSession: [] parameters: - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object additionalProperties: false properties: label: type: string maxLength: 100 spendLimit24hMicros: type: integer format: int64 minimum: 1 maximum: 10000000000 default: 100000000 spendFreezeHourMicros: type: integer format: int64 minimum: 1 maximum: 10000000000 default: 20000000 required: - label responses: '201': description: 已创建 content: application/json: schema: type: object additionalProperties: false properties: keyId: type: string format: uuid apiKey: type: string required: - keyId - apiKey '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' /v1/keys/{id}: delete: summary: 用邮箱密码账户会话撤销所属客户的 API Key;不能撤销最后一把有效密钥 security: - portalSession: [] parameters: - $ref: '#/components/parameters/KeyID' - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } responses: '204': description: 已撤销 '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' get: tags: - Keys summary: 查询单个 API Key(访问其他 Key 需要 account 权限;过期 Key 可查询自身) responses: '200': description: Key 元数据 content: application/json: schema: $ref: '#/components/schemas/APIKeyMeta' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid security: - bearerAuth: [] - portalSession: [] put: tags: - Keys summary: 用邮箱密码账户会话修改 Key 权限与消费安全额度,或解除异常冻结(有效期不可修改) responses: '200': description: 修改后的 Key content: application/json: schema: $ref: '#/components/schemas/APIKeyMeta' '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: true requestBody: required: true content: application/json: schema: type: object description: 整体覆盖;scopes、ipAllowlist 与 version 缺一不可,漏传或为 null 返回 400 INVALID_KEY_REQUEST 且不修改 required: - label - scopes - ipAllowlist - version properties: label: type: string scopes: type: array description: 空数组表示全部权限 items: type: string ipAllowlist: type: array description: 空数组表示不限来源 items: type: string version: type: integer minimum: 1 description: 读取时的版本号,不一致返回 409 VERSION_CONFLICT spendLimit24hMicros: type: integer format: int64 minimum: 1 maximum: 10000000000 spendFreezeHourMicros: type: integer format: int64 minimum: 1 maximum: 10000000000 clearSpendFreeze: type: boolean description: 核对任务和账本后解除异常冻结;不会清除过去一小时的消费窗口 security: - portalSession: [] /v1/usage: get: summary: 按任务创建时间统计消费和预占,最多查询 90 天 security: - bearerAuth: [] parameters: - in: query name: keyId description: 仅统计或查询当前客户此 API Key 的任务;省略时包含所有 Key 和历史未记录 Key 的任务 schema: type: string maxLength: 64 - in: query name: from schema: type: string format: date-time - in: query name: to schema: type: string format: date-time - in: query name: granularity schema: type: string enum: - day description: 按日拆分,结果项带 date - in: query name: tz schema: type: string description: granularity=day 时的 IANA 时区,缺省 UTC responses: '200': description: 用量统计 content: application/json: schema: type: object properties: from: type: string format: date-time to: type: string format: date-time currency: type: string items: type: array items: $ref: '#/components/schemas/Usage' '400': $ref: '#/components/responses/Error' /v1/topup-orders: post: summary: 已停用;旧客户端收到 410 TOPUP_ORDERS_DISABLED security: - bearerAuth: [] parameters: - in: header name: Idempotency-Key required: true schema: type: string minLength: 8 maxLength: 128 requestBody: required: true content: application/json: schema: type: object additionalProperties: false properties: amountMicros: type: integer format: int64 minimum: 100000 maximum: 1000000000000 method: type: string enum: - manual required: - amountMicros - method responses: '410': { $ref: '#/components/responses/Error' } '400': $ref: '#/components/responses/Error' get: summary: 查询自己的入金订单 security: - bearerAuth: [] responses: '200': description: 订单列表 content: application/json: schema: type: object properties: orders: type: array items: $ref: '#/components/schemas/TopupOrder' /v1/topup-orders/{id}: get: summary: 查询自己的入金订单 security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/OrderID' responses: '200': description: 订单 content: application/json: schema: $ref: '#/components/schemas/TopupOrder' '404': $ref: '#/components/responses/Error' put: summary: 修改仍可编辑的历史付款订单;不能用此接口创建新订单 security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/OrderID' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/TopupOrderPatch' } responses: '200': description: 更新后的订单 content: application/json: schema: { $ref: '#/components/schemas/TopupOrder' } '400': { $ref: '#/components/responses/Error' } '403': { $ref: '#/components/responses/Error' } '404': { $ref: '#/components/responses/Error' } '409': { $ref: '#/components/responses/Error' } /v1/tasks/{id}/cancel: post: summary: 只取消尚未派发的 queued 任务并原路退回预占余额 security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/TaskID' responses: '200': description: 已取消 content: application/json: schema: $ref: '#/components/schemas/Task' '404': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' /v1/webhook: get: summary: 查询自己的 HTTPS 回调配置,不返回签名密钥 security: - bearerAuth: [] responses: '200': description: 回调配置 content: application/json: schema: $ref: '#/components/schemas/WebhookConfig' '404': $ref: '#/components/responses/Error' put: summary: 设置或轮换 HTTPS 回调,签名密钥仅返回一次 security: - bearerAuth: [] requestBody: required: true content: application/json: schema: type: object additionalProperties: false properties: url: type: string format: uri maxLength: 2048 required: - url responses: '200': description: 回调配置和新签名密钥 content: application/json: schema: allOf: - $ref: '#/components/schemas/WebhookConfig' - type: object properties: signingSecret: type: string '400': $ref: '#/components/responses/Error' delete: summary: 删除回调配置并停止未投递事件 security: - bearerAuth: [] responses: '204': description: 已删除 /v1/webhook/deliveries: get: summary: 查看自己的回调投递记录 security: - bearerAuth: [] responses: '200': description: 投递记录 content: application/json: schema: type: object properties: deliveries: type: array items: $ref: '#/components/schemas/WebhookDelivery' /v1/webhook/deliveries/{id}/retry: post: summary: 修复回调配置后重试 dead 事件 security: - bearerAuth: [] parameters: - in: path name: id required: true schema: type: integer format: int64 responses: '202': description: 已重新入队 '404': $ref: '#/components/responses/Error' /v1/topup-orders/{id}/cancel: post: summary: 取消自己的 pending 入金订单 security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/OrderID' responses: '200': description: 订单已取消 content: application/json: schema: $ref: '#/components/schemas/TopupOrder' '404': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' /v1/keys/policy: get: tags: - Keys summary: 客户可见的 Key 策略 responses: '200': description: 策略 content: application/json: schema: type: object properties: customerKeyCreation: type: boolean maxActiveKeys: type: integer maxValidityDays: type: integer description: 自助创建的最长有效期,0 表示长期有效 customerRenewal: type: boolean reminderDays: type: array items: type: integer '401': $ref: '#/components/responses/Error' security: - bearerAuth: [] - portalSession: [] /v1/keys/renewal-plans: get: tags: - Keys summary: 自助续费套餐(过期 Key 也可查询) responses: '200': description: 套餐 content: application/json: schema: type: object properties: enabled: type: boolean plans: type: array items: $ref: '#/components/schemas/RenewalPlan' currency: type: string '401': $ref: '#/components/responses/Error' security: - bearerAuth: [] - portalSession: [] /v1/keys/{id}/reveal: post: tags: [Keys] summary: 再次查看 API Key 明文 description: 仅接受邮箱密码账户会话,且须二次验证:已开启 TOTP 的账户提交 code(动态验证码或一次性恢复码),否则提交登录密码。失败计入账户锁定计数;每次查看写入审计日志 api_key.reveal。只有 viewable 为 true 的 Key 可查看;历史 Key 可通过 POST /v1/portal/account/bind-key 粘贴一次启用查看。吊销后不可查看。响应带 Cache-Control no-store。 security: [{ portalSession: [] }] parameters: - in: path name: id required: true schema: { type: string, format: uuid } - in: header name: X-SolveHub-Portal required: true schema: { type: string, enum: ['1'] } requestBody: required: true content: application/json: schema: type: object properties: password: { type: string, description: 未开启 TOTP 时必填 } code: { type: string, description: 已开启 TOTP 时必填的动态验证码或一次性恢复码 } responses: '200': description: Key 明文 content: application/json: schema: type: object required: [keyId, apiKey] properties: keyId: { type: string, format: uuid } apiKey: { type: string } '400': { description: INVALID_PASSWORD、TOTP_REQUIRED 或 TOTP_INVALID } '403': { description: 非账户会话(ACCOUNT_SESSION_REQUIRED)或账户未绑定(ACCOUNT_UNBOUND) } '404': { $ref: '#/components/responses/Error' } '409': { description: Key 已吊销(KEY_REVOKED)或未保存可查看明文(KEY_NOT_VIEWABLE) } '429': { description: 账户已被临时锁定(LOGIN_THROTTLED) } /v1/keys/{id}/renew: post: tags: - Keys summary: 按套餐续费并从余额扣款;过期 Key 不能用自身凭证授权续费 description: 可使用账户会话、有效的自身 Key,或另一把有效且有 account 权限的 Key。以过期 Key 发起时返回 403 RENEWAL_REAUTH_REQUIRED;续期从当前时间与原到期时间中较晚者起算。 responses: '201': description: 续费成功 content: application/json: schema: $ref: '#/components/schemas/KeyRenewal' '200': description: '幂等重放(响应头 Idempotent-Replayed: true),未再次扣费' content: application/json: schema: $ref: '#/components/schemas/KeyRenewal' '402': description: INSUFFICIENT_BALANCE $ref: '#/components/responses/Error' '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: Idempotency-Key schema: type: string minLength: 8 maxLength: 128 description: 幂等键;网络重试时复用同一个值 required: true - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false requestBody: required: true content: application/json: schema: type: object properties: plan: type: string required: - plan security: - bearerAuth: [] - portalSession: [] /v1/keys/{id}/renewals: get: tags: - Keys summary: 续费记录 responses: '200': description: 记录 content: application/json: schema: type: object properties: renewals: type: array items: $ref: '#/components/schemas/KeyRenewal' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - $ref: '#/components/parameters/Limit' security: - bearerAuth: [] - portalSession: [] /v1/portal/session: post: tags: - Portal summary: 用 API Key 登录客户控制台,签发 HttpOnly 会话 Cookie(12 小时绝对有效、2 小时空闲失效) responses: '200': description: 会话 headers: Set-Cookie: schema: type: string content: application/json: schema: $ref: '#/components/schemas/PortalSession' '429': description: LOGIN_THROTTLED;Retry-After 为剩余秒数 $ref: '#/components/responses/Error' '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '503': $ref: '#/components/responses/Error' parameters: - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false requestBody: required: true content: application/json: schema: type: object properties: apiKey: type: string required: - apiKey security: [] get: tags: - Portal summary: 当前控制台会话 responses: '200': description: 会话 content: application/json: schema: $ref: '#/components/schemas/PortalSession' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' security: - portalSession: [] delete: tags: - Portal summary: 退出控制台并清除 Cookie responses: '204': description: 已退出 '403': $ref: '#/components/responses/Error' parameters: - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false security: - portalSession: [] /v1/portal/sessions: get: tags: - Portal summary: 本客户仍有效的控制台会话 responses: '200': description: 会话列表 content: application/json: schema: type: object properties: sessions: type: array items: $ref: '#/components/schemas/CustomerSession' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' security: - bearerAuth: [] - portalSession: [] /v1/portal/sessions/{id}: delete: tags: - Portal summary: 使用邮箱密码账户会话在其他设备上退出(撤销指定会话) responses: '204': description: 已撤销 '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: true security: - portalSession: [] /v1/webhooks: get: tags: - Webhooks summary: 回调端点列表 responses: '200': description: 端点 content: application/json: schema: type: object properties: endpoints: type: array items: $ref: '#/components/schemas/WebhookEndpoint' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' security: - bearerAuth: [] - portalSession: [] post: tags: - Webhooks summary: 新增回调端点;签名密钥只在响应中出现一次 responses: '201': description: 新端点与密钥 content: application/json: schema: type: object properties: endpoint: $ref: '#/components/schemas/WebhookEndpoint' secret: type: string '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' parameters: - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false requestBody: required: true content: application/json: schema: type: object properties: url: type: string description: type: string events: type: array items: type: string enabled: type: boolean security: - bearerAuth: [] - portalSession: [] /v1/webhooks/{id}: get: tags: - Webhooks summary: 查询回调端点 responses: '200': description: 端点 content: application/json: schema: $ref: '#/components/schemas/WebhookEndpoint' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid security: - bearerAuth: [] - portalSession: [] put: tags: - Webhooks summary: 修改回调端点 responses: '200': description: 端点 content: application/json: schema: $ref: '#/components/schemas/WebhookEndpoint' '400': $ref: '#/components/responses/Error' '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false requestBody: required: true content: application/json: schema: type: object properties: url: type: string description: type: string events: type: array items: type: string enabled: type: boolean security: - bearerAuth: [] - portalSession: [] delete: tags: - Webhooks summary: 删除回调端点 responses: '204': description: 已删除 '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false security: - bearerAuth: [] - portalSession: [] /v1/webhooks/{id}/rotate-secret: post: tags: - Webhooks summary: 轮换签名密钥;旧密钥在宽限期内继续签名 responses: '200': description: 新密钥 content: application/json: schema: type: object properties: secret: type: string previousSecretExpiresAt: type: string format: date-time '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false security: - bearerAuth: [] - portalSession: [] /v1/webhooks/{id}/test: post: tags: - Webhooks summary: 向端点发送一条测试事件 responses: '202': description: 已排队 content: application/json: schema: type: object properties: eventId: type: integer '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' '404': $ref: '#/components/responses/Error' '409': $ref: '#/components/responses/Error' parameters: - in: path name: id required: true schema: type: string format: uuid - in: header name: X-SolveHub-Portal schema: type: string enum: - '1' description: 使用控制台会话 Cookie 的写请求必须为 1 required: false security: - bearerAuth: [] - portalSession: [] /v1/topup-orders/instructions: get: tags: - Billing summary: 已停用;返回 410 PAYMENT_METHODS_DISABLED responses: '410': { $ref: '#/components/responses/Error' } '401': $ref: '#/components/responses/Error' '403': $ref: '#/components/responses/Error' security: - bearerAuth: [] - portalSession: [] /createTask: post: tags: - Compatibility summary: 兼容接口:创建任务 security: [] requestBody: required: true content: application/json: schema: type: object properties: clientKey: type: string description: SolveHub API Key task: type: object description: 兼容类型(ImageToTextTask、TurnstileTask*)或 SolveHub 类型名,其余字段见 README required: - clientKey - task responses: '200': description: 始终为 HTTP 200;errorId 为 0 表示成功,为 1 时见 errorCode content: application/json: schema: oneOf: - type: object properties: errorId: type: integer enum: - 0 taskId: type: string format: uuid - $ref: '#/components/schemas/CompatError' /getTaskResult: post: tags: - Compatibility summary: 兼容接口:查询结果 security: [] requestBody: required: true content: application/json: schema: type: object properties: clientKey: type: string description: SolveHub API Key taskId: type: string format: uuid required: - clientKey - taskId responses: '200': description: 始终为 HTTP 200;errorId 为 0 表示成功,为 1 时见 errorCode content: application/json: schema: oneOf: - type: object properties: errorId: type: integer enum: - 0 status: type: string enum: - processing - ready solution: type: object cost: type: string createTime: type: integer endTime: type: integer - $ref: '#/components/schemas/CompatError' /getBalance: post: tags: - Compatibility summary: 兼容接口:可用余额(美元) security: [] requestBody: required: true content: application/json: schema: type: object properties: clientKey: type: string description: SolveHub API Key required: - clientKey responses: '200': description: 始终为 HTTP 200;errorId 为 0 表示成功,为 1 时见 errorCode content: application/json: schema: oneOf: - type: object properties: errorId: type: integer enum: - 0 balance: type: number - $ref: '#/components/schemas/CompatError' components: securitySchemes: bearerAuth: type: http scheme: bearer portalSession: type: apiKey in: cookie name: solvehub_portal description: '客户控制台 HttpOnly 会话;写请求还需 X-SolveHub-Portal: 1' parameters: Limit: in: query name: limit schema: type: integer minimum: 1 maximum: 100 default: 50 TaskID: in: path name: id required: true schema: type: string format: uuid KeyID: in: path name: id required: true schema: type: string format: uuid OrderID: in: path name: id required: true schema: type: string format: uuid TicketIdempotencyKey: in: header name: Idempotency-Key required: false description: 可选幂等键,8 到 128 个可见 ASCII 字符(不含空格);按客户隔离,24 小时内有效。响应不确定(网络错误或 5xx)时用同一值和相同请求重试。 schema: type: string minLength: 8 maxLength: 128 pattern: '^[!-~]{8,128}$' responses: Error: description: 请求失败 content: application/json: schema: type: object required: - error properties: error: type: string schemas: DataDomeURLRequest: type: object additionalProperties: false required: [websiteURL] anyOf: - required: [body] - required: [location] - required: [captchaUrl] properties: websiteURL: type: string format: uri maxLength: 2048 description: 触发拦截的完整 HTTP(S) 页面 URL,不含账号密码,最多 2048 UTF-8 字节 body: type: string maxLength: 1048576 description: 捕获的原始 HTML 或 JSON 响应正文,最多 1 MiB UTF-8 字节 location: type: string maxLength: 4096 description: 捕获的 Location 响应头,最多 4096 UTF-8 字节 captchaUrl: type: string maxLength: 4096 description: 已知挑战 URL,优先于 body 和 location,最多 4096 UTF-8 字节 DataDomeURLResult: type: object additionalProperties: false required: [captchaUrl, challengeType] properties: captchaUrl: type: string format: uri description: 构建的 HTTPS captcha-delivery.com 挑战 URL,必须保留全部查询参数 challengeType: type: string enum: [captcha, interstitial] CreateTask: type: object required: - type - task additionalProperties: false properties: type: type: string enum: - ocr - turnstile - interstitial - perimeterx - datadome - akamai - aliyun - imperva - awswaf - alix5sec - funcaptcha - funcaptcha_classification - perimeterx_cookies task: type: object description: 对应验证码类型的任务字段;由服务端按 type 校验。turnstile、interstitial、datadome、akamai 支持可选 useragent(最多 512 UTF-8 字节);turnstile 选择家族及平台,其他三者还选择主版本。浏览器范围和示例见 /docs/useragents。DataDome 与 PerimeterX 处理软时限分别为 240 和 210 秒,从任务确认受理后起算。完整字段和示例见客户网站的 /docs。 Price: type: object required: - available properties: available: type: boolean priceMicros: type: integer format: int64 currency: type: string enum: - USD reason: type: string version: type: integer format: int64 updatedAt: type: string format: date-time QuotedPrice: description: 在 Price 基础上叠加客户协议价;version 与 updatedAt 仍是公开价目的版本信息。 allOf: - $ref: '#/components/schemas/Price' - type: object required: - negotiated properties: negotiated: type: boolean description: 为 true 时 priceMicros 是本客户的协议价 listPriceMicros: type: integer format: int64 description: 公开标价;仅在类型可用时返回 Task: type: object required: - taskId - type - status - priceMicros - traceId - createdAt properties: keyId: type: string format: uuid description: 创建任务的 API Key ID(非密钥明文);历史未记录时省略 taskId: type: string format: uuid type: type: string status: type: string enum: - queued - dispatching - processing - completed - failed - uncertain - cancelled priceMicros: type: integer format: int64 traceId: type: string format: uuid errorCode: type: string description: 仅返回平台任务错误码 TASK_FAILED、TASK_TIMEOUT 或 TASK_UNCERTAIN。 enum: - TASK_FAILED - TASK_TIMEOUT - TASK_UNCERTAIN failureReason: type: string description: 可选诊断文本。明确失败时返回脱敏后的上游说明(如 IP Blocked (fake page)),未提供说明时可回退为稳定失败码;不要作为枚举解析。 solution: type: object additionalProperties: true createdAt: type: string format: date-time finishedAt: type: string format: date-time Balance: type: object required: - availableMicros - reservedMicros - currency properties: availableMicros: type: integer format: int64 reservedMicros: type: integer format: int64 currency: type: string enum: - USD LedgerEntry: type: object required: - id - kind - availableDeltaMicros - reservedDeltaMicros - at properties: id: type: integer format: int64 taskId: type: string format: uuid kind: type: string enum: - credit - reserve - capture - refund reference: type: string availableDeltaMicros: type: integer format: int64 reservedDeltaMicros: type: integer format: int64 at: type: string format: date-time Event: type: object required: - traceId - from - to - message - at properties: traceId: type: string format: uuid from: type: string to: type: string message: type: string at: type: string format: date-time Account: type: object properties: customerId: type: string format: uuid name: type: string status: type: string enum: [active, frozen, closed] statusReason: type: string contactEmail: type: string contactEmailVerified: type: boolean createdAt: type: string format: date-time MeResponse: allOf: - $ref: '#/components/schemas/Account' - type: object properties: key: type: object properties: keyId: { type: string, description: 账户会话下为空字符串 } label: { type: string } scopes: type: array items: { type: string, enum: [tasks, billing, account] } expiresAt: { type: [string, 'null'], format: date-time } expired: { type: boolean } APIKeyMeta: type: object properties: keyId: type: string format: uuid label: type: string hint: type: string description: 创建时保存的前缀与末 4 位,如 sh_…Ab3x;旧 Key 无此字段 viewable: type: boolean description: 服务端保存了加密明文,账户会话二次验证后可通过 POST /v1/keys/{id}/reveal 再次查看;吊销后为 false status: type: string enum: - active - expired - revoked scopes: type: array items: type: string enum: - tasks - billing - account ipAllowlist: type: array items: type: string expiresAt: type: - string - 'null' format: date-time lastUsedAt: type: - string - 'null' format: date-time lastUsedIp: type: string createdBy: type: string createdAt: type: string format: date-time revokedAt: type: string format: date-time version: type: integer spendLimit24hMicros: type: integer format: int64 description: 过去 24 小时按任务预占累计的安全上限;默认 100 USD spendFreezeHourMicros: type: integer format: int64 description: 一小时累计预占触发自动冻结的阈值;默认 20 USD,不得大于 24 小时上限 spendFrozenAt: type: [string, 'null'] format: date-time description: 非空时该 Key 无法创建新任务;幂等重放仍可读取原任务 spendQuotaMicros: type: integer format: int64 description: 平台为该 Key 设置的总额度(累计预占上限,退款不返还);缺省表示不限制。仅管理员可设置 spentTotalMicros: type: integer format: int64 description: 设置总额度后该 Key 累计预占的金额 maxActiveTasks: type: integer description: 该 Key 的并发上限(排队与处理中的任务数),默认 5;试用 Key 通常为 1。仅管理员可调整 TopupOrder: type: object properties: orderId: type: string format: uuid customerId: type: string format: uuid amountMicros: type: integer format: int64 currency: type: string enum: - USD method: type: string enum: - manual status: type: string enum: - pending - paid - cancelled - rejected - expired paymentChannel: { type: string } payerReference: { type: string } customerNote: { type: string } externalReference: type: string closeReason: { type: string } createdAt: type: string format: date-time expiresAt: { type: [string, 'null'], format: date-time } settledAt: type: [string, 'null'] format: date-time closedAt: { type: [string, 'null'], format: date-time } TopupOrderPatch: type: object additionalProperties: false minProperties: 1 properties: paymentChannel: { type: string } payerReference: { type: string } customerNote: { type: string } Usage: type: object properties: keyId: type: string description: 仅指定 keyId 筛选时返回;未筛选时保持按任务类型汇总 date: type: string format: date description: granularity=day 时所选时区的自然日 type: type: string created: type: integer format: int64 completed: type: integer format: int64 failed: type: integer format: int64 cancelled: type: integer format: int64 uncertain: type: integer format: int64 chargedMicros: type: integer format: int64 reservedMicros: type: integer format: int64 WebhookConfig: type: object properties: url: type: string format: uri enabled: type: boolean updatedAt: type: string format: date-time WebhookDelivery: type: object properties: eventId: type: integer format: int64 taskId: type: string format: uuid eventType: type: string status: type: string enum: - pending - delivered - dead attempts: type: integer lastHttpStatus: type: integer lastError: type: string createdAt: type: string format: date-time deliveredAt: type: string format: date-time RenewalPlan: type: object properties: code: type: string name: type: string days: type: integer priceMicros: type: integer KeyRenewal: type: object properties: renewalId: type: string format: uuid keyId: type: string format: uuid customerId: type: string format: uuid source: type: string enum: - customer - admin planCode: type: string days: type: integer feeMicros: type: integer previousExpiresAt: type: - string - 'null' format: date-time newExpiresAt: type: string format: date-time actor: type: string note: type: string createdAt: type: string format: date-time PortalSession: type: object properties: authMethod: type: string enum: [api_key, account] sessionId: type: string format: uuid customerId: type: string format: uuid customerName: type: string customerStatus: type: string enum: - active - frozen - closed - unbound keyId: type: string format: uuid description: 账户登录时为空字符串 keyLabel: type: string scopes: type: array items: type: string keyExpiresAt: type: string format: date-time keyExpired: type: boolean description: 为 true 时只能访问续费、余额、账本与充值等接口 expiresAt: type: string format: date-time idleTimeoutSeconds: type: integer CustomerSession: type: object properties: authMethod: type: string enum: [api_key, account] id: type: string format: uuid keyId: type: string format: uuid description: 账户登录时为空字符串 keyLabel: type: string ip: type: string userAgent: type: string createdAt: type: string format: date-time lastSeenAt: type: string format: date-time expiresAt: type: string format: date-time current: type: boolean WebhookEndpoint: type: object properties: id: type: string format: uuid url: type: string description: type: string events: type: array items: type: string enabled: type: boolean primary: type: boolean createdAt: type: string format: date-time updatedAt: type: string format: date-time CompatError: type: object properties: errorId: type: integer enum: - 1 errorCode: type: string errorDescription: type: string