---
url: https://wiki.awmc.team/dev/awmc-api.md
---

# 🔌 AWMC 网关公共 API（计费说明）

面向**使用者**：如何调用开放接口，以及 **Token 何时会扣费**。本文不讨论上游编解码实现。

::: tip 平台地址
平台地址：https://api.wmc.pub

在线文档：https://wiki.awmc.team/dev/awmc-api

使用 **AWMC 通行证（论坛账号）** 登录控制台，在个人中心生成 `gw_` 令牌或使用登录 JWT。
:::

::: warning 🔐 鉴权
业务请求须在请求头携带：

`Authorization: Bearer <令牌>`

* 浏览器登录后的 JWT，或在网站内生成的 **`gw_` 长期令牌**（勿泄露）。
  :::

::: tip 购买 Token
额度通过 **卡密兑换** 充入账户。卡密可在[爱发电商城](https://afdian.com/a/AWMC_TEAM?tab=shop)购买。\
兑换：控制台个人中心，或 `POST /redeem`（需登录令牌）。
:::

::: info keychip
机台 `keychip` 由网关服务端注入，**调用方无需也不应传递**。请求体只需提供业务参数（如 `qrcode`）。
:::

::: danger 高风险操作（必读）
以下操作**极可能对账号产生不可逆转的严重后果**，请勿随意调用：

1. **改门**：`POST /v1/kaleidx-scope/upsert`（修改 Kaleidx Scope Gate 状态）。
2. **未验证收藏品写入**：通过 `POST /v1/item/upsert` 或 `POST /v1/user/upsert-all` 发送 **`itemKind` 为 `4`、`8`、`15` 等未充分验证的收藏品**数据。

网关不会替你拦截这些请求；调用即视为自行承担风险。
:::

## 1. 服务地址与路径兼容

所有业务路径接在 **网关根地址** 之后，前缀为 **`/v1`**。

* 上游已升级为 **AWMC API v2 / v2-3 双模式**；**对外路由保持旧路径不变**（例如仍用 `/v1/user/data`）。
* 网关会**自动探测**上游代次（约 60 秒缓存）：`GET /v1/health` 的响应带有 `upstreamVersion` 字段（`v2` 或 `v2-3`），以及 `sessionApi`（当前上游写接口是否为会话制）。
* **POST**：使用 **明文 JSON Body**（`Content-Type: application/json`）。zlib / Base64 由网关处理。
* 响应同时包含：
  * 上游字段：`returnCode`、`returnMessage`（成功时可能附带 `businessData`）
  * 兼容字段：业务成功时 **`code === 0`**，`msg` 尽量可二次 `JSON.parse`

**成功判定（扣费与业务）**

| 接口 | 上游成功条件 |
|------|----------------|
| `GET /v1/health` | `returnCode === 0`（ping） |
| 其它业务 | `returnCode === 1` |
| 会话制写接口（v2-3 上游） | 见 [§2.3 计费时机](#_2-3-会话制接口的计费时机-v2-3-上游) |

建议新客户端以 `returnCode` 为准；旧客户端可读 `code === 0`。

## 2. Token 计费规则

* 下表 **「消耗」**：本次请求在 **HTTP 2xx** 且上游业务成功时扣除的 Token；**0** 表示不扣费。
* 余额不足返回 **403** `Insufficient balance`。
* 建议客户端超时 **180** 秒（购买 Charge 等写入更慢；`?sync=1` 同步等待模式建议 200 秒）。
* **会话制写接口**（v2-3 上游下的所有 `upsert` 类）：**创建会话不扣费**，计费时机见 [§2.3](#_2-3-会话制接口的计费时机-v2-3-上游)；上游为旧版 v2 时仍为「业务成功即扣费」。

### 2.1 兼容旧路径

| 方法 | 路径 | 消耗 | 说明 |
|------|------|------|------|
| GET | `/v1/health` | 0 | 连通检查（映射 ping） |
| POST | `/v1/user/data` | 2 | 用户基础数据 |
| POST | `/v1/user/region` | 2 | 地区记录 |
| POST | `/v1/user/music` | 4 | 全部成绩 |
| POST | `/v1/user/charge` | 2 | 已持有 Charge（只读） |
| GET | `/v1/charge/queue` | 0 | **占位**：上游已无真实队列，固定返回空 `tasks` |
| POST | `/v1/charge` | 10 / 15 / 25 | 购买一张 Charge（`chargeId`/`charge` = **2 / 3 / 5** 时分别扣 **10 / 15 / 25** Token） |
| POST | `/v1/update-lx` | 5 | 同步到落雪 LXNS（`key`+`qrcode`；旧字段 `type` 可忽略） |
| POST | `/v1/update-fish` | 5 | 同步到 Diving-Fish（`token`+`qrcode`） |

配额分类中，`/v1/update-lx` 与 `/v1/update-fish` 属于**读取**（读取成绩并同步到外部服务）；其余 `upsert`、删除、清空和购票接口属于写入。详见[配额与限流](/dev/quota)。

### 2.2 新增路径

#### 基础读写（v2 时代新增）

| 方法 | 路径 | 消耗 | 说明 |
|------|------|------|------|
| POST | `/v1/user/preview` | 1 | 用户预览 |
| POST | `/v1/user/item-list` | 2 | 道具列表 |
| POST | `/v1/user/kaleidx-scope` | 2 | 读取 Gate 状态（仅 v2 上游；v2-3 返回 410，改用 `/v1/user/item-list`） |
| POST | `/v1/music/upsert` | 15 | 上传/覆盖成绩（v2：1–4 条同步返回；v2-3：**1–20 条，会话制**，见 §2.3） |
| POST | `/v1/music/delete` | 10 | 删除成绩（v2：1–4 条；v2-3：会话制，保留游玩次数累计） |
| POST | `/v1/item/upsert` | 20 | 添加/删除道具（**高风险见上文**；v2-3 支持**批量 `itemList`**，旧样式自动转换） |
| POST | `/v1/ticket/clear` | 5 | 清空 Charge（v2-3：会话制） |
| POST | `/v1/kaleidx-scope/upsert` | 30 | 改门（**高风险见上文**；v2-3 改映射 `upsert-kaleidx-gate`，`gateId` 限 **1–6**） |
| POST | `/v1/user/upsert-all` | 25 | 合并写入（**仅 v2 上游**；v2-3 已移除，返回 410，请拆分为单接口） |

#### 会话控制（v2-3 上游）

| 方法 | 路径 | 消耗 | 说明 |
|------|------|------|------|
| POST | `/v1/upsert/continue` | 0 | 确认上传会话。**宽容幂等**：会话已被网关自动确认时直接返回当前状态，不会报错 |
| POST | `/v1/upsert/check` | 0 | 轮询上传会话状态（幂等只读可重试）；`upsert_status=2` 时**触发结算**（见 §2.3） |

#### rival 免会话读取（rival-direct 补丁上游）

| 方法 | 路径 | 消耗 | 说明 |
|------|------|------|------|
| POST | `/v1/rival/data` | 2 | 读取对手资料（游戏名等；无需 `qrcode` / `keychip`，见 §3.9） |
| POST | `/v1/rival/music` | 4 | 拉取对手全部成绩汇总（每曲含每谱明细；无 FC/FS 字段，见 §3.9） |

#### 资源查询（v2-3 上游，无需账号参数、同步返回）

| 方法 | 路径 | 消耗 | 说明 |
|------|------|------|------|
| POST | `/v1/user/ping` | 0 | 游戏连接检查（`get-ping`，v2-3 专属；可用于判断上游代次） |
| POST | `/v1/select/categories` | 1 | 资源分类列表 |
| POST | `/v1/select/resource` | 1 | 按分类分页查询资源（`category` 必填；`query` / `offset` / `limit` 可选） |
| POST | `/v1/select/music` | 1 | 分页查询曲目与谱面常量（含 `charts`，`constant` 定数） |
| POST | `/v1/select/music-unlock-plan` | 1 | 乐曲解锁候选清单（**资源候选**，非账号实时缺失清单） |

#### 账号资料修改（v2-3 上游，均会话制）

| 方法 | 路径 | 消耗 | 说明 |
|------|------|------|------|
| POST | `/v1/music/upsert-fuzzy` | 15 | 模糊上传成绩（`dxStar` 半星档 0–7，`dxScore=0` 时生效） |
| POST | `/v1/user/play-count` | 8 | 设置游玩次数（`playCount` 累计 / `currentPlayCount` 当前版本） |
| POST | `/v1/user/maimile` | 8 | 设置 maimile 点数（0–99999） |
| POST | `/v1/user/map-stock` | 8 | 设置地图库存（0–999） |
| POST | `/v1/user/rating` | 10 | 设置显示 Rating（不提升历史最高） |
| POST | `/v1/user/course-rank` | 8 | 设置段位（0–23，同时补通关记录） |
| POST | `/v1/user/class-rank` | 8 | 设置阶级（0–25，保留历史最高） |
| POST | `/v1/user/chara-slot` | 8 | 设置出击角色栏（0/1/5 个角色 ID） |
| POST | `/v1/user/character` | 8 | 修改角色等级 / 觉醒 / 使用次数 |
| POST | `/v1/user/intimate` | 8 | 修改搭档亲密度（`partnerId` 用查询返回的 `naviCharaId`） |
| POST | `/v1/user/map-complete` | 15 | 直接完成指定地图并补奖励（**高风险**） |
| POST | `/v1/user/map-challenge` | 15 | 推进地图到未获得挑战曲前（不允许进度回退，**高风险**） |
| POST | `/v1/user/login-bonus` | 8 | 设置登录奖励到差一个盖章 |
| POST | `/v1/version/reset` | 10 | 重置账号到当前游戏版本（不代替乐曲解锁） |

### 2.3 会话制接口的计费时机（v2-3 上游）

上游为 **v2-3** 时，所有写接口（成绩 / 道具 / 发票 / 资料）都是**会话制三步**：创建 → 确认 → 轮询。网关把其中两步做掉了：

```
① 创建    POST /v1/music/upsert（等业务接口）
          → 网关转发上游，返回会话对象（upsert_status=0，60 秒确认窗口）
② 确认    网关【自动】立即调用 upsert-continue，无需调用方操作
          → 返回给调用方的响应已是「已确认、后台上传中」（upsert_status=1）
③ 轮询    POST /v1/upsert/check  { "session_id": "..." }     ← 调用方自己做
          → upsert_status: 1 处理中 / 2 完成 / 3 错误
```

**计费规则**（与旧版 v2「成功即扣费」不同）：

* **创建与确认阶段不扣费**；
* 仅当 `/v1/upsert/check` 返回 `upsert_status === 2`（完成）时，按创建接口的定价**结算一次**（同一 `session_id` 永远只结算一次，幂等）；
* `upsert_status === 3`（上传失败）**不扣费**；
* `/v1/upsert/check` 与 `/v1/upsert/continue` 本身**消耗为 0**。

**会话响应结构**（`businessData`，创建 / 确认 / 查询均返回）：

```json
{
  "session_id": "32位小写十六进制",
  "upsert_kind": "upsert-music-exact",
  "upsert_status": 1,
  "upsert_message": {
    "text": "后台上传中",
    "phase": "uploading",
    "total_rounds": 2,
    "completed_rounds": 1,
    "estimated_remaining_seconds": 30
  }
}
```

`upsert_message.result`（完成时）含 `verified`、`uploadedMusicCount`、`skippedMusicCount`、`noOp` 等结果字段；失败时读 `upsert_message.error`。**创建成功 ≠ 上传完成**，请务必轮询到终态。

失败终态里 `errorCode` 为 **`5006` / `5007` / `5104`** 时特殊：上游**已把数据写入、只是核对没跑完**（保存验证失败 / 上传中断），**结果可能已落库**——请先核对账号数据（或重查同一 `session_id`）再决定是否重发。

**`?sync=1` 同步模式**：给任何会话制写接口加查询参数 `?sync=1`（如 `POST /v1/music/upsert?sync=1`），网关会代为轮询直到终态（默认预算 180 秒）后**同步返回最终结果**——行为与旧版 v2 的同步语义一致，适合不想实现轮询的简单客户端。

**410 能力缺失**：当接口在当前上游代次不存在时，网关返回 `410` 与 `UPSTREAM_CAPABILITY_MISSING`（如 v2-3 上游下的 `/v1/user/upsert-all`、`/v1/user/game-event`、`/v1/user/kaleidx-scope`，或 v2 上游下的 `/v1/select/*`、`/v1/upsert/*`）。

**字段白名单**：v2-3 上游严格校验请求字段，多余字段会返回 `1205`。网关已按接口过滤（旧客户端的 `qr_text`、`type` 等历史遗留字段会被自动剔除），调用方按本文字段表传参即可。

::: tip 控制台可视化
不想自己写轮询界面？登录 [api.wmc.pub 控制台](https://api.wmc.pub/dashboard.html)，侧边栏「**上传会话**」页面可直接查看会话状态（摘要卡 + 状态徽章 + 行内实时刷新，存在处理中会话时每 5 秒自动轮询）；对应 API 见 [§5.2](#_5-2-上传会话查询-需鉴权)。
:::

### 充值 / 票券行为变更

1. `POST /v1/charge` 不再「入队」，而是直接购买 Charge；Body 可用 `charge` 或 `chargeId`，**仅允许 2 / 3 / 5**（2倍票 / 3倍票 / 5倍票），分别扣 **10 / 15 / 25** Token，其它值返回 400。
2. `GET /v1/charge/queue` 保留路径但**无真实任务**；请勿依赖队列状态轮询。
3. 查询已持有票券请用 `POST /v1/user/charge`。

## 3. 开放接口调试

在下方 **鉴权设置** 中填入有效令牌，再填写参数测试。

### 3.1 健康检查（不计费）

### 3.2 用户查询（计费 / JSON Body）

以下接口均为 **POST**，Body 字段 **`qrcode`**。

### 3.3 购买 Charge 与队列占位

### 3.4 成绩写入（传歌曲）— 详细说明

> **不要和** `POST /v1/user/music`（**查询**全部成绩）搞混。\
> **写入 / 覆盖**请用 `POST /v1/music/upsert`；**删除**用 `POST /v1/music/delete`；**模糊上传**用 `POST /v1/music/upsert-fuzzy`。

#### 一次请求能干什么？

* Body 里带 `qrcode` + `musicList`（数组）。
* `musicList` 长度：**v2 上游 1～4 条**；**v2-3 上游 1～20 条**（服务端自动分轮）。
* 每一项用 **`(musicId, level)`** 唯一确定一条谱面成绩。
* 消耗 **15 Token**；**v2 上游**成功条件为 `returnCode === 1`（同步返回）；**v2-3 上游**为会话制（见 §2.3），返回会话对象后轮询 `/v1/upsert/check`，或加 `?sync=1` 同步等待。

#### `level`（难度）

| 值 | 难度 |
|---:|------|
| 0 | BASIC |
| 1 | ADVANCED |
| 2 | EXPERT |
| 3 | MASTER |
| 4 | Re:MASTER |
| 10 | 宴会场 UTAGE |

#### 精确 / 模糊两种模式（最容易搞错）

**v2 上游**（旧契约，`fuzzy` 布尔开关）：

| 模式 | `fuzzy` | `achievement` | `dxScore` 含义 |
|------|---------|---------------|----------------|
| **精确** | `false` | 目标达成率，如 `100.9444` | **实际 DX 分数**（如 `2947`） |
| **模糊** | `true` | 希望达到的**最低**达成率 | **DX 星级 0～5**（不是实际 DX 分！） |

**v2-3 上游**（新契约，精确 / 模糊拆成两个接口）：

| 接口 | 说明 |
|------|------|
| `/v1/music/upsert`（→ `upsert-music-exact`） | 精确模式：`achievement` 推荐协议整数（`100% = 1000000`，**整数 100 不是 100%**）；`dxScore` 为实际 DX 分，不得超过谱面上限 |
| `/v1/music/upsert-fuzzy`（→ `upsert-music-fuzzy`） | 模糊模式：`dxStar` **半星档 0～7**（`dxScore=0` 时生效）；不足四位的百分比小数会在缺失位随机补 1–9 |

`dxStar` 最低完成度：`0` 不限 · `1` 85% · `1.5` 87.5% · `2` 90% · `2.5` 91.5% · `3` 93% · `3.5` 94% · `4` 95% · `4.5` 96% · `5` 97% · `5.5` 98% · `6` 99% · `6.5` 99.5% · `7` 100%。

::: tip 旧客户端兼容
v2 时代「`musicList` 项内 `fuzzy: true`」的老写法在 v2-3 上游下仍可用——网关检测到后会**自动改投** `upsert-music-fuzzy` 并清理该字段；`fuzzy` 标记必须整批一致，否则返回 400。
:::

#### Combo / Sync 枚举

* Combo：`none` / `fc` / `fcp` / `ap` / `app`（v2 可传字符串；v2-3 传整数 0～4）
* Sync：`none` / `fs` / `fsp` / `fsd` / `fsdp` / `sync`（v2 可传字符串；v2-3 传整数 0～5）

#### 最小可用示例（v2-3 精确模式）

```json
{
  "qrcode": "SGWCMAID...",
  "musicList": [
    {
      "musicId": 11479,
      "level": 3,
      "achievement": 1009444,
      "dxScore": 2100,
      "comboStatus": 4,
      "syncStatus": 0
    }
  ]
}
```

#### 模糊模式示例（v2-3，dxStar 半星档）

```json
{
  "qrcode": "SGWCMAID...",
  "musicList": [
    { "musicId": 11176, "level": 2, "achievement": 1000000, "dxScore": 0, "dxStar": 3, "comboStatus": 2, "syncStatus": 0 }
  ]
}
```

`musicId` 必须换成当前版本真实存在的曲目 ID，否则上游可能返回 `5004`；`dxScore` 超过谱面上限返回 `5009`。

更完整的字段表与示例也可在 [API 调试 / Swagger](/dev/api-docs) 中打开 **Score → `/v1/music/upsert`** 查看。

### 3.4b 会话轮询（v2-3 上游）

会话制写接口返回的 `businessData.session_id` 用在这两个接口上：

### 3.5 高风险写入（请先阅读顶部警告）

#### 道具写入契约（v2-3 上游）

v2-3 支持**批量 `itemList`**（同 `itemKind`/`itemId` 不重复）：

```json
{
  "qrcode": "SGWCMAID...",
  "itemList": [
    { "itemKind": 5, "itemId": 10030, "stock": 1, "isValid": true }
  ]
}
```

* `itemKind`：1 姓名牌 / 2 称号 / 3 头像 / 4 礼物 / 5 乐曲 / 6 MASTER / 7 Re:MASTER / 10 搭档 / 11 底板
* 普通道具 `stock=1, isValid=true` 拥有、`stock=0, isValid=false` 移除
* 礼物（`itemKind=4`）的 `stock` 为**绝对库存**（非负整数），`itemId` 编码 = 来源类型 × 1000000 + 资源 ID
* 全部跳过时上游返回 `noOp=true`，不上传

::: tip 旧样式兼容
v2 时代的单件写法 `{ "itemKind": 2, "itemId": 11, "operation": "add" }`（`operation`：`add` / `del`）在 v2-3 上游下仍可用，网关自动转换为单元素 `itemList`。
:::

### 3.6 成绩上传到第三方

### 3.7 资源查询（v2-3 上游）

无需账号参数（不用传 `qrcode`），同步返回，每接口消耗 1 Token：

### 3.8 账号资料修改（v2-3 上游，会话制）

示例：设置游玩次数 / Rating。其余接口（`maimile`、`character`、`map-*` 等）见 [§2.2 账号资料修改表](#_2-2-新增路径)，均为会话制（同 §2.3：创建 → 自动确认 → 轮询）：

### 3.9 rival 免会话成绩读取（rival-direct 补丁上游）

> **2026-10-17 起**，上游 rival 接口经实测为**免会话直连**：不需要
> 二维码（`qrcode`）也不用 `keychip`，直接按**好友码**（即 userid）拉取
> 对手资料与成绩。需要 rival-direct 补丁版上游（原版 v2-3 无这些端点，
> 旧 v2 上游返回 410）。
>
> **官方协议天生限制**：rival 成绩只有**达成率 + DX 分**，没有 FC / FS /
> 同步状态字段；响应可能含 `musicId=0` 幽灵条目与 `achievement=0` 空成
> 绩，调用方需过滤。

**字段说明**

* `achievement`：万分制整数（`997601` = 99.7601%）；`deluxscoreMax` 为 DX 分。
* `level`：`0`–`4` = BASIC–Re:Master；宴场为 `10`（仅 `includeUtage: true` 时返回）。
* `musicId >= 10000` 为 DX 谱面，`< 10000` 为标准谱。
* 计费按 **read 配额**；上游失败（如密钥轮换后未更新 EXE 基线）返回 `errorCode` 5110–5113 的 `SdgbError`。

**curl 示例**

```bash
curl -X POST https://api.wmc.pub/v1/rival/music \
  -H "Authorization: Bearer <你的令牌>" \
  -H "Content-Type: application/json" \
  -d '{"rivalId": 13225939}'
```

## 4. 公开 JSON 目录

```http
GET https://api.wmc.pub/api/docs
```

返回各路径、方法、**消耗** 与简要说明（含风险提示文案）。

## 5. 调用用量与失败率

### 5.1 用量统计（需鉴权）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/me/usage` | 本人调用明细分页 |
| GET | `/me/usage/stats` | 本人日粒度统计；`days`=7/14/30 |

### 5.2 上传会话查询（需鉴权）

v2-3 上游下本人的上传会话（也可在**控制台侧边栏「上传会话」页面**可视化查看：状态徽章、实时刷新、自动轮询）：

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/me/upsert-sessions` | 本人会话分页（`limit` 默认 50 / `offset` / `status` 0-3），响应含 `summary` 状态汇总（pending/processing/completed/failed/billed） |
| POST | `/me/upsert-sessions/refresh` | Body `sessionId`：向上游实时查询该会话并同步表内状态；`upsert_status=2` 时幂等结算；上游异常时仍返回表行 + `error` 字段 |

### 5.3 失败率（半小时精度）

| 方法 | 路径 | 鉴权 | 范围 |
|------|------|------|------|
| GET | `/usage/failure-rate` | 无需 | 全站 |
| GET | `/me/usage/failure-rate` | Bearer | 本人 |

固定近 7 天、30 分钟一桶。`codeZero` 表示业务成功计数（ping 的 `returnCode=0` 或其它业务的 `1`）。

## 6. 常见错误

| HTTP / returnCode | 说明 |
|------|------|
| **401** | 令牌缺失或无效 |
| **403** | 余额不足等 |
| **410** | 接口在当前上游代次不可用（`UPSTREAM_CAPABILITY_MISSING`，见 §2.3） |
| **4001** 等 | 上游业务错误（如用户正在登录中），见 `returnMessage` |
| **5004 / 5009** | 谱面不存在 / DX 分超出谱面上限 |
| **5101 / 5106** | 会话不存在或已清理 / 会话未在 60 秒内确认（v2-3；网关自动确认下罕见） |
| **5110–5113** | rival 免会话直连失败（网络 / HTTP 非 200 / 响应解密失败 / 响应异常；解密失败多为 WAHLAP 密钥轮换，需更新上游 EXE 基线后重打补丁） |
| **5006 / 5007 / 5104** | 上游保存验证失败 / 上传中断（v2-3 会话终态）——**数据可能已落库**，请先核对账号数据，勿盲目重发 |
| **500 / 502** | 转发或解码失败 |

::: tip 建议
先调用 **`/v1/health`**（看 `upstreamVersion` 判断上游代次）；再调用查询类接口。\
购买票券用 **`/v1/charge`**，不要依赖 **`/v1/charge/queue`**。\
v2-3 上游下：会话制写接口拿到 `session_id` 后**务必轮询** `/v1/upsert/check` 到 `upsert_status` 为 2/3；忘记轮询不会丢结果（会话保留在上游，可随时续查），但计费只发生在轮询看到 `2` 时。\
切勿在日志中记录二维码与第三方 Token。
:::
