Skip to content

AWMC Gateway Public API (Billing) ​

For end users: how to call open endpoints and when Tokens are charged. This page does not cover upstream wire encoding.

Platform

Platform: https://api.wmc.pub

Docs: https://wiki.awmc.team/en/dev/awmc-api

Sign in with an AWMC passport (forum account), then create a gw_ token or use the login JWT.

Auth

Send:

Authorization: Bearer <token>

  • Session JWT after browser login, or a long-lived gw_ token from the console (keep secret).

Buy Tokens

Top up with card codes from the Afdian shop.
Redeem in the console or via POST /redeem.

keychip

keychip is injected by the gateway. Callers must not send it. Provide business fields only (e.g. qrcode).

High-risk operations (read first)

These actions may cause irreversible, severe damage to an account. Do not call them casually:

  1. Gate edits: POST /v1/kaleidx-scope/upsert (Kaleidx Scope Gate state).
  2. Unverified collectibles: writing itemKind 4 / 8 / 15 and other poorly verified collectible types via POST /v1/item/upsert or POST /v1/user/upsert-all.

The gateway does not block these requests; you accept the risk by calling them.

1. Base URL & path compatibility ​

All business paths are under /v1 on the gateway.

  • Upstream is AWMC API v2 / v2-3 dual mode; public paths stay unchanged (e.g. still /v1/user/data).
  • The gateway auto-detects the upstream generation (~60s cache): GET /v1/health responds with upstreamVersion (v2 or v2-3) and sessionApi (whether writes are session-based).
  • POST with plain JSON (Content-Type: application/json). zlib/Base64 is handled by the gateway.
  • Responses include:
    • Upstream: returnCode, returnMessage (and businessData when applicable)
    • Compatibility: code === 0 on business success; msg often JSON-parseable

Success (billing)

EndpointUpstream success
GET /v1/healthreturnCode === 0 (ping)
Other businessreturnCode === 1
Session-based writes (v2-3 upstream)See §2.3 billing timing

Prefer returnCode in new clients; legacy clients may use code === 0.

2. Token pricing ​

Charged on HTTP 2xx and upstream business success. Suggest 180s client timeout (200s with ?sync=1).

Session-based write endpoints (all upsert ones on a v2-3 upstream): creation is free; billing timing in §2.3. On a legacy v2 upstream they still bill on success.

2.1 Legacy-compatible paths ​

MethodPathCostNotes
GET/v1/health0Connectivity (ping)
POST/v1/user/data2User data
POST/v1/user/region2Region records
POST/v1/user/music4All scores
POST/v1/user/charge2Owned Charges (read-only)
GET/v1/charge/queue0Stub: no real queue; empty tasks
POST/v1/charge10 / 15 / 25Buy one Charge (chargeId/charge = 2 / 3 / 5 → 10 / 15 / 25 Tokens)
POST/v1/update-lx5LXNS sync (key+qrcode; ignore legacy type)
POST/v1/update-fish5Diving-Fish sync

2.2 New paths ​

Basic reads/writes (v2 era) ​

MethodPathCostNotes
POST/v1/user/preview1Preview
POST/v1/user/item-list2Items
POST/v1/user/kaleidx-scope2Read Gate state (v2 only; 410 on v2-3, use /v1/user/item-list)
POST/v1/music/upsert15Upsert scores (v2: 1–4 sync; v2-3: 1–20, session-based, see §2.3)
POST/v1/music/delete10Delete scores (v2: 1–4; v2-3: session-based, keeps play counts)
POST/v1/item/upsert20Item write (high risk; v2-3 supports batch itemList, legacy style auto-converted)
POST/v1/ticket/clear5Clear Charges (v2-3: session-based)
POST/v1/kaleidx-scope/upsert30Gate edit (high risk; v2-3 maps to upsert-kaleidx-gate, gateId limited to 1–6)
POST/v1/user/upsert-all25Combined write (v2 only; removed on v2-3 → 410, split into single endpoints)

Session control (v2-3 upstream) ​

MethodPathCostNotes
POST/v1/upsert/continue0Confirm an upload session. Tolerant idempotent: if already auto-confirmed by the gateway, returns current state without error
POST/v1/upsert/check0Poll session status (idempotent read); upsert_status=2 triggers settlement (see §2.3)

rival sessionless reads (rival-direct patched upstream) ​

MethodPathCostNotes
POST/v1/rival/data2Rival profile (player name etc.; no qrcode / keychip needed, see §3.9)
POST/v1/rival/music4Rival full score summary (per-chart details; no FC/FS fields, see §3.9)

Resource queries (v2-3 upstream; no account params, sync) ​

MethodPathCostNotes
POST/v1/user/ping0Game connectivity check (get-ping, v2-3 only; useful to detect the generation)
POST/v1/select/categories1Resource categories
POST/v1/select/resource1Paged resources by category (category required; query / offset / limit optional)
POST/v1/select/music1Paged songs with chart constants (charts, constant)
POST/v1/select/music-unlock-plan1Unlock candidates (resource candidates, not live account gaps)

Profile writes (v2-3 upstream; all session-based) ​

MethodPathCostNotes
POST/v1/music/upsert-fuzzy15Fuzzy score upload (dxStar half-star 0–7, applies when dxScore=0)
POST/v1/user/play-count8Play count (playCount total / currentPlayCount current version)
POST/v1/user/maimile8maimile points (0–99999)
POST/v1/user/map-stock8Map stock (0–999)
POST/v1/user/rating10Displayed rating (does not raise the historical best)
POST/v1/user/course-rank8Course rank (0–23, also adds clear records)
POST/v1/user/class-rank8Class rank (0–25, keeps historical best)
POST/v1/user/chara-slot8Character slot (0/1/5 character IDs)
POST/v1/user/character8Character level / awakening / use count
POST/v1/user/intimate8Partner intimacy (partnerId = naviCharaId from queries)
POST/v1/user/map-complete15Complete maps and grant rewards (high risk)
POST/v1/user/map-challenge15Advance map to first unearned challenge song; no progress rollback (high risk)
POST/v1/user/login-bonus8Set login bonus to one stamp before claim
POST/v1/version/reset10Reset account to the current game version (does not unlock songs)

2.3 Billing timing for session-based writes (v2-3 upstream) ​

On a v2-3 upstream every write endpoint (scores / items / tickets / profile) is a three-step session: create → confirm → poll. The gateway does two of them for you:

① Create   POST /v1/music/upsert (or other business endpoint)
           → gateway forwards and returns a session (upsert_status=0, 60s confirm window)
② Confirm  the gateway AUTO-calls upsert-continue immediately — no caller action needed
           → the response you get is already "confirmed, uploading" (upsert_status=1)
③ Poll     POST /v1/upsert/check  { "session_id": "..." }     ← caller does this
           → upsert_status: 1 processing / 2 complete / 3 error

Billing rules (different from legacy v2 "bill on success"):

  • Create and confirm stages are free;
  • Settlement happens once when /v1/upsert/check returns upsert_status === 2 (complete), at the creating endpoint's price (idempotent per session_id);
  • upsert_status === 3 (upload failure) is not billed;
  • /v1/upsert/check and /v1/upsert/continue themselves cost 0.

Session response shape (businessData, returned by create / confirm / poll):

json
{
  "session_id": "32-char lowercase hex",
  "upsert_kind": "upsert-music-exact",
  "upsert_status": 1,
  "upsert_message": {
    "text": "Uploading in background",
    "phase": "uploading",
    "total_rounds": 2,
    "completed_rounds": 1,
    "estimated_remaining_seconds": 30
  }
}

upsert_message.result (on completion) contains verified, uploadedMusicCount, skippedMusicCount, noOp; on failure read upsert_message.error. Session created ≠ upload complete — always poll to a terminal state.

One failure case is special: when the terminal errorCode is 5006 / 5007 / 5104, the upstream already wrote the data and only its verification pass didn't finish (save-verification failure / upload interruption), so the result may already be persisted — check the account data (or re-query the same session_id) before resending.

?sync=1 mode: append ?sync=1 to any session-based write (e.g. POST /v1/music/upsert?sync=1) and the gateway polls on your behalf until the terminal state (default budget 180s) and returns the final result synchronously — same semantics as legacy v2, for simple clients that don't want to implement polling.

410 capability missing: when an endpoint doesn't exist on the current upstream generation, the gateway returns 410 with UPSTREAM_CAPABILITY_MISSING (e.g. /v1/user/upsert-all, /v1/user/game-event, /v1/user/kaleidx-scope on v2-3; or /v1/select/*, /v1/upsert/* on v2).

Field whitelist: the v2-3 upstream strictly validates request fields; extra fields return 1205. The gateway filters per endpoint (legacy junk like qr_text, type is stripped automatically) — just send the fields listed on this page.

Console visualization

Don't want to build your own polling UI? Sign in to the api.wmc.pub console — the sidebar "Upsert Sessions" page shows session status directly (summary tiles + status badges + per-row live refresh, auto-polling every 5s while any session is processing); see the corresponding API in §5.

Charge / queue behavior change ​

  1. POST /v1/charge buys a Charge directly (not enqueue); 10 / 15 / 25 Tokens for chargeId 2 / 3 / 5.
  2. GET /v1/charge/queue remains but has no real tasks.
  3. Use POST /v1/user/charge to list owned tickets.

3. Interactive demos ​

3.1 Health ​

GETHealth
/v1/health▼

3.2 User reads ​

POSTUser data
/v1/user/data▼

3.3 Buy Charge & queue stub ​

POSTBuy Charge
/v1/charge▼

3.4 Score writes (upload charts) — details ​

Do not confuse with POST /v1/user/music (read all scores).
Write/overwrite → POST /v1/music/upsert; delete → POST /v1/music/delete; fuzzy → POST /v1/music/upsert-fuzzy.

What one request does ​

  • Body: qrcode + musicList (array).
  • musicList length: 1–4 on v2; 1–20 on v2-3 (server auto-splits rounds).
  • Each item is uniquely identified by (musicId, level).
  • Costs 15 Tokens; v2: success when upstream returnCode === 1 (sync). v2-3: session-based (see §2.3) — poll /v1/upsert/check afterwards, or add ?sync=1 to wait synchronously.

level ​

ValueDifficulty
0BASIC
1ADVANCED
2EXPERT
3MASTER
4Re:MASTER
10UTAGE

Exact vs fuzzy — easy to get wrong ​

v2 upstream (legacy contract, fuzzy boolean):

ModefuzzyachievementMeaning of dxScore
ExactfalseTarget rate, e.g. 100.9444Actual DX score (e.g. 2947)
FuzzytrueMinimum desired rateDX star 0–5 (not the raw DX score!)

v2-3 upstream (new contract, split into two endpoints):

EndpointNotes
/v1/music/upsert (→ upsert-music-exact)Exact: achievement as protocol integer recommended (100% = 1000000; integer 100 is NOT 100%); dxScore is the raw DX score, must not exceed the chart max
/v1/music/upsert-fuzzy (→ upsert-music-fuzzy)Fuzzy: dxStar half-star 0–7 (applies when dxScore=0); percentages with fewer than 4 decimals get random 1–9 padding

dxStar minimum completion: 0 none · 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%.

Legacy client compatibility

The v2-era style of fuzzy: true inside musicList items still works on a v2-3 upstream — the gateway detects it and reroutes to upsert-music-fuzzy (stripping the flag). The flag must be consistent across the batch, otherwise 400.

Combo / Sync enums ​

  • Combo: none / fc / fcp / ap / app (strings on v2; integers 0–4 on v2-3)
  • Sync: none / fs / fsp / fsd / fsdp / sync (strings on v2; integers 0–5 on v2-3)

Minimal example (v2-3 exact) ​

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

Fuzzy example (v2-3, dxStar) ​

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

musicId must exist in the current version, otherwise 5004; dxScore over the chart max returns 5009.

More detail: API Reference / Swagger → Score → /v1/music/upsert.

POSTUpsert scores (exact)
/v1/music/upsert▼

3.4b Session polling (v2-3 upstream) ​

Use the businessData.session_id from a session-based write with these two endpoints:

POSTPoll session (settlement point)
/v1/upsert/check▼

3.5 High-risk writes ​

Item write contract (v2-3 upstream) ​

v2-3 supports batch itemList (no duplicate itemKind/itemId in one batch):

json
{
  "qrcode": "SGWCMAID...",
  "itemList": [
    { "itemKind": 5, "itemId": 10030, "stock": 1, "isValid": true }
  ]
}
  • itemKind: 1 name plate / 2 title / 3 icon / 4 present / 5 music / 6 MASTER / 7 Re:MASTER / 10 partner / 11 frame
  • Regular items: stock=1, isValid=true to own, stock=0, isValid=false to remove
  • Presents (itemKind=4): stock is the absolute stock (non-negative integer); itemId = source type × 1000000 + resource ID
  • If everything is skipped, the upstream returns noOp=true and uploads nothing

Legacy style compatibility

The v2-era single-item style { "itemKind": 2, "itemId": 11, "operation": "add" } (operation: add / del) still works on a v2-3 upstream; the gateway converts it into a one-element itemList.

POSTGate edit (high risk)
/v1/kaleidx-scope/upsert▼

3.6 Third-party sync ​

POSTUpload to LXNS
/v1/update-lx▼

3.7 Resource queries (v2-3 upstream) ​

No account params (no qrcode), sync responses, 1 Token each:

POSTSongs & chart constants
/v1/select/music▼

3.8 Profile writes (v2-3 upstream, session-based) ​

Examples: play count / rating. Other endpoints (maimile, character, map-*, …) are listed in §2.2; all session-based (§2.3: create → auto-confirm → poll):

POSTSet play count
/v1/user/play-count▼

3.9 rival sessionless reads (rival-direct patched upstream) ​

Since 2026-10-17 the upstream rival endpoints are verified sessionless: no QR code (qrcode) and no keychip — pull rival profile and scores directly by friend code (= userid). Requires the rival-direct patched upstream (stock v2-3 lacks these endpoints; legacy v2 upstream returns 410).

Official protocol limitation: rival scores carry achievement + DX score only — no FC / FS / sync status fields; responses may contain musicId=0 phantom rows and achievement=0 empty rows, which callers should filter out.

POSTRival profile
/v1/rival/data▼

Field notes

  • achievement: 1/10000-scale integer (997601 = 99.7601%); deluxscoreMax is the DX score.
  • level: 0–4 = BASIC–Re:MASTER; Utage is 10 (only returned with includeUtage: true).
  • musicId >= 10000 is a DX chart, < 10000 standard.
  • Billed against the read quota; upstream failures (e.g. after a WAHLAP key rotation before the EXE baseline is updated) return SdgbError with errorCode 5110–5113.

curl example

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

4. Public JSON catalog ​

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

5. Usage & failure rate ​

5.1 Usage ​

MethodPathAuthScope
GET/me/usageBearerPersonal log
GET/me/usage/statsBearerDaily stats

5.2 Upsert sessions (v2-3 upstream) ​

Your own upload sessions on a v2-3 upstream (also viewable in the console sidebar page "Upsert Sessions" with status badges, live refresh and auto-polling):

MethodPathAuthScope
GET/me/upsert-sessionsBearerOwn sessions, paginated (limit default 50 / offset / status 0-3); response includes a summary (pending/processing/completed/failed/billed)
POST/me/upsert-sessions/refreshBearerBody sessionId: live upstream check + table sync; idempotent settlement on upsert_status=2; upstream errors still return the row + error field

5.3 Failure rate ​

MethodPathAuthScope
GET/usage/failure-rateNoneSite-wide, 7d / 30m buckets
GET/me/usage/failure-rateBearerPersonal failure rate

codeZero counts business success (returnCode 0 for ping, 1 otherwise).

6. Common errors ​

HTTP / returnCodeMeaning
401Missing/invalid token
403Insufficient balance
410Endpoint unavailable on the current upstream generation (UPSTREAM_CAPABILITY_MISSING, see §2.3)
4001 etc.Upstream business errors (see returnMessage)
5004 / 5009Chart not found / DX score over chart max
5101 / 5106Session missing or cleaned / not confirmed within 60s (v2-3; rare with gateway auto-confirm)
5110–5113rival sessionless direct-call failure (network / non-200 HTTP / response decryption failure / malformed response; decryption failures usually mean a WAHLAP key rotation — update the upstream EXE baseline and re-patch)
5006 / 5007 / 5104Upstream save-verification failure / upload interruption (v2-3 session terminal) — the result may already be persisted; verify the account data first, don't blindly resend
500 / 502Forwarding or decode failure

Tips

Start with /v1/health (check upstreamVersion for the generation). Buy tickets with /v1/charge; do not rely on /v1/charge/queue.
On a v2-3 upstream: after a session-based write returns a session_id, always poll /v1/upsert/check until upsert_status is 2/3 — forgetting to poll doesn't lose the result (sessions stay queryable), but settlement only happens when a poll sees 2. Never log QR codes or third-party tokens.

Released under the MIT License.