---
title: "REST APIリファレンス"
description: "SDKとCLIは、このHTTP APIの薄いクライアントです。JavaScript SDKのないエンジンから、自分のサーバーから、あるいはツールから直接使えます。"
url: "/ja/docs/reference/rest-api/"
lang: ja
index: "/ja/llms.txt"
---
# REST APIリファレンス

SDKとCLIは、このHTTP APIの薄いクライアントです。JavaScript SDKのないエンジンから、自分のサーバーから、あるいはツールから直接使えます。ベースURLは自分のAPI（`https://api.kumodeck.com`）です。ボディはすべてJSONです。

## 認証

| 呼び出し元 | ヘッダー |
|---|---|
| プレイヤー（アプリやゲームにログインしている利用者） | `X-Kumo-Key: pk_…` + `Authorization: Bearer <access token>` |
| 公開のアプリのデータ | `X-Kumo-Key: pk_…`のみ |
| サーバー / CLI / CI | `X-Kumo-Key: sk_…` |
| 開発者（ダッシュボード） | `Authorization: Bearer kds_…` |

エラーは常に次の形です。

```json
{ "error": { "code": "version_conflict", "message": "…", "details": { "currentVersion": 7 } } }
```

APIでは、アプリを使う人（利用者）を *players*（プレイヤー。`/v1/players/…`、`playerId`）と呼びます。このページもその名前のまま書きます。

**どのオリジンからでも。** SDKは、どこでホストしたアプリやゲーム（KUMODeckのホスティング、自分のサーバー、itch.io、localhost）からでも動きます。CORSは開放されていて、認証はヘッダーで行います。ほかのサイトに自分の公開鍵を使わせないようにするには、`kumo.config.json` → `web.allowedOrigins`に自分のオリジンを列挙します（[設定](/ja/docs/reference/config/index.md)を参照）。以後、ほかのオリジンからのブラウザのリクエストは403 `origin_not_allowed`になります。どのモジュールも単独で動きます。セーブだけ、マルチプレイだけ、ホスティングだけ、といった使い方ができます。

**機能はONにするまでOFFです**（[設定](/ja/docs/reference/config/index.md)の`features`）。OFFの機能をアプリや秘密鍵から呼ぶと、何かを読み書きする前に403 `feature_disabled`（`details: { feature, configPath }`。例: `features.saves`）が返ります。ゲストのログイン、トークンの更新、自分のプレイヤー情報、設定の読み取りはいつでも使えます。
今は提供していない機能は403 `feature_unavailable`を返し、ONにすることもできません。

自分が所有していないリソースは403ではなく**404**を返すので、存在そのものは明かされません。期限切れのアクセストークンは401 `token_expired`を返します。更新してから再試行してください。残高を変えるエンドポイントは`Idempotency-Key`ヘッダー（`A-Z a-z 0-9 _ . : -`からなる1〜128文字）を受け付けます。同じキーで再試行すると、二重に適用せず最初の結果を返します。

## プレイヤーと認証

| メソッド | パス | ボディ → レスポンス |
|---|---|---|
| POST | `/v1/auth/guest` | `{ displayName? }` → セッション |
| POST | `/v1/auth/email/signup` | `{ email, password (≥ 8), displayName? }` → セッション |
| POST | `/v1/auth/email/login` | `{ email, password }` → セッション |
| POST | `/v1/auth/refresh` | `{ refreshToken }` → セッション（回転済み）。401 = 系列が失効、409 `refresh_race` = 別のタブが直前に回転させた |
| POST | `/v1/auth/logout` | `{ refreshToken }` |
| POST | `/v1/auth/link/email` | `{ email, password }` → `{ player }`（ゲストのデータはすべて残る） |
| GET / PATCH | `/v1/players/me` | → `{ player }` / `{ displayName }` |

**セッション**は`{ player: { id, displayName, isGuest, createdAt, identities }, accessToken, expiresIn, refreshToken }`です。アクセストークンの有効期間は15分、リフレッシュトークンは90日（回転式）です。

メールの確認、パスワードの再設定、外部サービスでのログイン、プライバシー:

| メソッド | パス | ボディ → レスポンス |
|---|---|---|
| POST | `/v1/auth/email/verify` | `{ token }` — 確認リンクから取得 |
| POST | `/v1/auth/email/resend` | `{ redirectUrl? }`（プレイヤー） |
| POST | `/v1/auth/password/forgot` | `{ email, redirectUrl? }` — 常に200 |
| POST | `/v1/auth/password/reset` | `{ token, password }` — そのプレイヤーのリフレッシュトークンをすべて失効させる |
| POST / GET | `/v1/auth/oauth/:provider/start` | `:provider` = `google` \| `discord` \| `apple` \| `x`。`{ redirectUrl, challenge, mode? }` → `{ url }`。プレイヤーの`Authorization`ヘッダーを付けると、そのプレイヤーにプロバイダーが**連携**されます。GET（クエリに`key=pk_…`）はリダイレクトするので、普通のリンクに使えます |
| GET / POST | `/v1/auth/oauth/:provider/callback` | プロバイダーがここに戻り、その後`<redirectUrl>#kumo_code=…`（または`#kumo_error=…`）へリダイレクト |
| POST | `/v1/auth/oauth/exchange` | `{ code, verifier }` → セッション（コードの有効期間は60秒、1回限り） |
| GET | `/v1/players/me/identities` | → `{ identities }`（ログイン手段。ゲストは含まない） |
| DELETE | `/v1/players/me/identities/:provider` | `{ refreshToken }`（この端末のもの）→ `{ player }`。この端末以外はすべてログアウト。409 `last_login_method` = 最後のログイン手段（先に別の手段を追加する） |
| GET | `/v1/players/me/export` | そのプレイヤーについて保存しているすべて（JSON） |
| DELETE | `/v1/players/me` | `{ password }`または`{ refreshToken }` — 取り消し不可 |

`redirectUrl`は設定の`auth.redirectUrls`で許可されている必要があります（開発中のlocalhostと、KUMODeckでホストしているURLは常に許可されます）。プロバイダーは`auth.providers`で有効にするまで404 `provider_disabled`を返し、認証情報が足りない間（Google、Discord、Apple）は409 `provider_not_configured`を返します。[ログイン・認証](/ja/docs/guides/auth/index.md)を参照してください。

## ゲームデータ（プレイヤー）

| メソッド | パス | 補足 |
|---|---|---|
| GET | `/v1/gamedata/definitions` | 設定のうち公開してよい部分。`pk_`のみ |
| GET | `/v1/saves` | `{ saves: [{ key, version, size, updatedAt }] }` |
| GET | `/v1/saves/:key` | `{ key, version, size, updatedAt, data }` · 無ければ404 |
| PUT | `/v1/saves/:key` | `{ data, ifVersion? }` · 409 `version_conflict`、413 `save_too_large`、409 `too_many_saves` |
| DELETE | `/v1/saves/:key?ifVersion=` | |
| GET | `/v1/stats` | `{ stats: { key: value } }`（全期間） |
| POST | `/v1/stats` | `{ stats: { key: value } }`（64キーまで） → `{ stats: { key: { period: value } }, rejected, unlocked }` |

## マルチプレイ

| メソッド | パス | 補足 |
|---|---|---|
| GET (WebSocket) | `/v1/realtime` | 最初のフレーム`{ "t": "auth", "key": "pk_…", "token": "<access token>" }`を5秒以内に送る |
| GET | `/v1/rooms?mode=` | 参加受付中の公開ルーム`{ rooms: [{ id, code, mode, players, maxPlayers, metadata, createdAt }] }` |

WebSocketのリクエストには任意で`rid`を付けられ、`{ t: "reply", rid, ok, data | error }`が返ります。

| `t` | フィールド |
|---|---|
| `create` | `mode, private?, maxPlayers?, metadata?, code?, hostOnlyState?` |
| `join` | `roomId`または`code` |
| `quickMatch` / `cancelMatch` | `mode` / — |
| `leave`, `resume` | —, `roomId` |
| `send` | `type (≤ 64), data? (≤ 16 KB), to?: playerId[]` |
| `setState` / `setMyState` | `patch`（nullでキーを削除） |
| `lock` | `locked`（ホストのみ） |
| `signal` | P2Pのルームのみ: `to: playerId, kind: offer \| answer \| candidate \| restart, data?（8 KB以下）, gen?`。その1人へ中継される |
| `iceServers` | P2Pのルームのみ → `{ iceServers, iceTransportPolicy: 'relay' \| 'all', expiresAt }`・`turn_unavailable` |

サーバーからのイベント: `welcome`、`room_joined`（スナップショット。常にreplyより先に届く）、`player_joined`、`player_left`、`player_disconnected`、`player_reconnected`、`host_changed`、`state`、`player_state`、`message`、`locked`、`signal`（P2Pのルーム）、`room_closed`、`warn`。切断コード: 4001 未認証、4002 トークン期限切れ（更新して再接続）、4003 BAN / 禁止、4004 新しい接続に置き換えられた、4008 アイドル、4029 レート制限の乱用。

## リアルタイムの部屋

`features.realtimeChannels`をONにするまではOFFです（[ガイド](/ja/docs/guides/realtime/index.md)）。同じ`/v1/realtime`のWebSocketを使います（1接続で、マルチプレイのルーム1つと部屋8つまで）。何も保存しません。メッセージは届けたら捨てます。プレイヤーIDの無い接続は`{ "t": "auth", "key": "pk_…", "anon": true }`を送り、自分のサーバーが`open`にした部屋にだけ入れます。

| `t` | フィールド | エラー |
|---|---|---|
| `channelJoin` | `channel` | `channel_not_found`、`channel_forbidden`、`channel_banned`、`channel_full`、`player_required`、`too_many_channels` |
| `channelCreate` | `channel?`（`@…`。省くと自動で付く）、`join?`、`send?`、`maxMembers?`、`voice?`（`true`か`{ mode: 'sfu' }`） | `channel_exists`、`player_required`、`voice_too_large`、`feature_disabled`（`features.voice`） |
| `channelLeave` | `channel` | — |
| `channelSend` | `channel, type (≤ 64), data?` | `not_in_channel`、`muted`、`send_forbidden`、`payload_too_large`、`rate_limited` |
| `channelModerate` | `channel, op, playerId, seconds?` — `op`: `invite`、`uninvite`、`allowSend`、`disallowSend`、`mute`、`unmute`、`kick`、`ban`、`unban` | `owner_only` |
| `voiceJoin` / `voiceLeave` | `channel` — [音声通話](/ja/docs/guides/voice/index.md)。これと通話のほかのメッセージ（`voiceSignal`。中継サーバーの通話では`voicePublish`、`voiceAnswer`、`voiceSpeaking`）はSDKが送る | `voice_disabled`、`voice_suspended`、`voice_unavailable`、`feature_disabled`、`player_required`、`not_in_channel`、`rate_limited` |

サーバーからのイベント: `channel_joined`（設定と今いる人。常にreplyより先に届く）、`channel_message`（`from` = プレイヤーIDか`'server'`、`at` = サーバーの時刻）、`channel_player_joined`、`channel_player_left`、`channel_self`（`canSend`、`mutedUntil`）、`channel_closed`（`reason`）。音声: `voice_state`（`members: [{ id, canSpeak }]`）、`voice_signal`、`voice_sfu_offer`（中継サーバーの通話）、`voice_closed`（`time_limit`、`suspended`、`unavailable`）。`channel_joined`に`voice: { mode: 'p2p' | 'sfu', relayOnly } | null`。

自分のサーバーから（秘密鍵のみ。委任トークンでは使えません）:

| メソッド | パス | 補足 |
|---|---|---|
| PUT | `/v1/realtime/channels/:name` | `@`の名前のみ: `{ join?, send?, open?, maxMembers?, allow?: playerId[], speakers?: playerId[], voice?: true \| { mode?: 'p2p' \| 'sfu', relayOnly? } }` → 設定、名簿、今いる人・409 `voice_in_use` |
| GET | `/v1/realtime/channels/:name` | 同じもの（メッセージの本文は無い）・404 `channel_not_found` |
| DELETE | `/v1/realtime/channels/:name` | `{ deleted: true }`（今いる人には`channel_closed` `deleted`） |
| POST | `/v1/realtime/channels/:name/messages` | `{ type, data? }` → `{ delivered }`（`from: 'server'`で届く）・鍵ごとに1分600回 |
| POST | `/v1/realtime/channels/:name/moderate` | `{ op, playerId, seconds? }` → `{}`・鍵ごとに1分300回（PUT / DELETEと合わせて） |

## 自分のiOS / Androidアプリ（プレイヤー）

| メソッド | パス | 補足 |
|---|---|---|
| POST | `/v1/auth/oauth/:provider/native` | `apple` / `google`: `{ idToken, nonce, platform, link?, name? }` → セッション + `{ provider, linked }` |
| POST | `/v1/players/me/age-signal` | OSの年齢シグナル（`{ platform, status, lowerBound?, upperBound?, source?, supervised? }`）。年齢区分を厳しくする方向にしか変えない |

## 年齢区分（プレイヤー）

| メソッド | パス | 補足 |
|---|---|---|
| GET | `/v1/social/profile` | `{ ageBand, declaredAgeBand, capabilities }` |
| PUT | `/v1/social/age` | `{ band: child \| teen \| adult }`（プレイヤーは厳しくする方向にしか変えられない: 403 `age_change_not_allowed`） |

## BANへの異議申し立て（プレイヤー）

BANしたプレイヤーは、そのBANを確認して1回だけ異議を申し立てられます。申し立ては開発者として読んで回答します（下の表）。受け入れるとBANが解除されます。異議申し立てはBANの一部なので、いつでも使えます。

| メソッド | パス | 補足 |
|---|---|---|
| GET | `/v1/players/me/sanctions` | プレイヤー本人へのBAN（公開メモのみ） |
| POST | `/v1/players/me/sanctions/:sanctionId/appeal` | `{ message }` → 201 · BAN 1件につき1回 |
| POST | `/v1/auth/appeal` | `{ refreshToken, sanctionId?, message }` — BANされたプレイヤー向け（BANで失効したリフレッシュトークンで本人確認する。sanctionId を省くと、そのトークンを失効させたBANが対象） |

`POST /v1/stats`は、あなたの不正対策ルール（設定の`integrity.stats`: 値の範囲、1分あたりの送信回数、任意で自分のサーバーへの署名付きwebhook）も実行します。拒否された値は理由とともに`rejected`で返ります。

## Xでのシェア（プレイヤー / 公開）

ここにある機能はどれも、`kumo.config.json` → `share`で**ONにするまでOFF**です（[設定](/ja/docs/reference/config/index.md)と[ガイド](/ja/docs/guides/sharing/index.md)を参照）。OFFの機能は404 `share_<tool>_disabled`を返し、SDKはこれを「使っていない」として扱います。

| メソッド | パス | 認証 | 補足 |
|---|---|---|---|
| POST | `/v1/share/links` | プレイヤー | `{ kind: plain \| score \| challenge, score?, showName? }` → 201 `{ link, imageUrl, post: { text, hashtags, via } }`。 |
| GET | `/v1/share/links/:shareId` | `pk_` | `{ link: { id, kind, score, displayName, createdAt } }` — 受け取った側で挑戦を復元する |
| POST | `/v1/share/visits` | `pk_`（サインイン中ならプレイヤーも） | `{ shareId, landed }` — プレイヤーごとに1日1回だけ数える（`share.tracking`） |
| GET | `/v1/share/images/:label/card.png` · `…/:shareId.png` | なし | 1200×600のPNGカード（`share.images`）。`:label` = 自分のslug、developmentでは`<slug>--dev`。キャッシュされる。不明なIDは404 |
| GET | `/v1/share/tags?ks=` | `pk_`または`sk_` | `{ html, tags, image, defaultImage, shareImageTemplate }` — ページの`<head>`に書く`<meta>`タグ（シェアごとのカードは、リクエストごとに読む） |
| GET | `/v1/players/me/x-profile` · PUT `{ visible }` | プレイヤー | プレイヤーのXのハンドル・名前・アバターと、表示を許可しているかどうか（既定: 表示しない） |
| GET | `/v1/players/x-profiles?ids=a,b` | `pk_` | `{ profiles: { [playerId]: { username, name, avatarUrl } } }` — 許可したプレイヤーのみ（IDは最大100個） |
| POST | `/v1/auth/transfer` | プレイヤー | → 201 `{ code, expiresIn: 600 }` — 別のブラウザで同じプレイヤーとして続けるための1回限りのコード（`share.inAppBrowser`） |
| POST | `/v1/auth/transfer/redeem` | `pk_` | `{ code }` → セッション |

KUMODeckでホストしているアプリは、同じカード画像をアプリ自身のドメインでも配信します: `/.card.png`と`/.card/<shareId>.png`。Xでのログインは、`:provider` = `x`で通常のOAuthのルートを使います（[ログイン・認証](/ja/docs/guides/auth/index.md)を参照）。ダッシュボードに自分のXアプリの資格情報が必要で、ない場合は`POST /v1/auth/oauth/x/start`が409 `provider_not_configured`を返します。ログインは無料（Xが料金を請求しない）なので、いまは支払いを理由に断られることはありません。将来Xが料金を取るようになった場合に限り、KUMODeckの共用Xアプリを使うアプリは残高が支払い期限を過ぎている間402 `x_signin_unavailable`（`details.continueAsGuest: true`）を返します。そのときはプレイヤーをゲストアカウントのまま続けさせてください。

## サーバー（秘密鍵）

| メソッド | パス | 補足 |
|---|---|---|
| GET / PUT | `/v1/config` | 鍵の環境のマスターデータを読む / pushする |
| POST | `/v1/deployments` | `{ files: [{ path, sha256, size }], message? }` → `{ deploymentId, version, missing, missingBytes }` |
| PUT | `/v1/blobs/:sha256` | 足りないファイル1つの生のバイト列（50MBまで） |
| POST | `/v1/deployments/:ref/finalize` | `{ activate?: true }` · 409 `blobs_missing` |
| POST | `/v1/deployments/:ref/activate` | 公開中のバージョンを切り替える（ロールバック / 昇格） |
| GET | `/v1/deployments?limit=` · `/v1/deployments/:ref` · `/v1/hosting` | 履歴、1つのバージョン（マニフェスト付き）、公開中のURL |

`:ref`はデプロイのIDまたはバージョン番号です。

## Functions（秘密鍵またはダッシュボード）

自分のサーバーのプログラムとデータベースです（[ガイド](/ja/docs/guides/functions/index.md)）。**既定はOFF**です。各パスは、`X-Kumo-Key: sk_…`なら`/v1/admin/<path>`で、開発者セッションなら`/v1/projects/:projectId/environments/:env/<path>`で使えます。

| メソッド | `<path>` | 補足 |
|---|---|---|
| GET | `functions` | `{ enabled, suspended, limits, deployed, version, url, crons, durableObjects, resources, secrets }`（シークレットは名前のみ） |
| POST | `functions/enable` · `functions/disable` | enable: `{ cpuMs?, subRequests? }` · 前払いのクレジットが無いと402 `prepaid_required` · disableしてもコードとデータは残る |
| PUT | `functions/limits` | `{ cpuMs? (1–30000), subRequests? (0–1000) }` |
| POST | `functions/deployments` | `{ mainModule, modules: [{ name, type, content (base64) }], bindings, vars, crons, compatibilityDate?, message? }` → 201 `{ version, … }`。通常は`kumodeck functions deploy`が送る |
| GET | `functions/deployments?limit=` | 履歴（新しい順） |
| DELETE | `functions?purge=true` | コードを削除する。`purge`を付けるとデータベース、KV、ファイル、キューも削除する |
| GET / PUT / DELETE | `functions/secrets` · `functions/secrets/:name` | 値はそのままランタイムに渡され、保存も返却もされない |
| POST | `functions/db/:binding/query` · `functions/db/:binding/migrate` | `{ sql, params? }` · `{ migrations: [{ name, sql }] }` → `{ applied, failed, skipped }` |

Functionsのコードから（秘密鍵のみ）: `POST /v1/admin/players/verify-token` `{ token }` → `{ player: { id, displayName, banned } }`（別の環境のトークンなら401）。コードからBANするときは`POST /v1/admin/players/:playerId/ban` · `/unban`（下の運営の表）。

## 開発者（ダッシュボードのセッション）

| メソッド | パス | |
|---|---|---|
| POST | `/v1/developers/signup` · `/login` · `/logout` · GET `/v1/developers/me` | アカウント（`me`に`hasPassword`）。`signup`は任意の`inviteCode`を受ける（無効なコードならアカウントを作らない: 400 `invalid_invite_code` / 409 `invite_code_used` / `invite_limit_reached`） |
| POST | `/v1/developers/me/invite-code` | `{ code }` → `{ credited: 500, currency: "usd", pending }` — 招待クレジット（利用料だけに使えます・返金や出金はできません）。メールの確認が済むと入る（それまでは`pending: true`）。開発者セッションのみ（MCPからは不可）。409 `already_redeemed`（1アカウントに1回） |
| POST | `/v1/developers/oauth/:provider/start` · `/v1/developers/oauth/exchange` | `exchange`は新しいアカウントのときだけ任意の`inviteCode`を受ける（既存のアカウントなら`invite: { ignored: true }`）。Google・GitHubのログイン、ログイン方法の追加（`intent: "link"`）、本人の確認のやり直し（`intent: "reauth"`）。PKCE。使えるプロバイダは`GET /v1/platform/info`の`oauthProviders` |
| GET / DELETE / POST | `/v1/developers/me/identities` · `/me/identities/:provider` · `/me/password` | ログイン方法: 一覧・解除（`password`か`reauthToken`が必要。最後の1つは外せない）・パスワードの設定 / 変更 |
| GET / POST | `/v1/projects` | 一覧 / 作成（すべての鍵を平文で**一度だけ**返す） |
| GET / DELETE | `/v1/projects/:projectId` | |
| PATCH | `/v1/projects/:projectId` | `{ name }`（1〜80文字）→ `{ project, previousName, changed }`。プロジェクトの名前を変える（名前は利用者に届くメールの件名と差出人に出る）。スラッグは変わらない。KUMODeckや運営を思わせる名前は400 `name_reserved`（作成でも同じ）。1時間に20回を超えると429 |
| POST | `/v1/projects/:projectId/environments/:env/keys` | 新しい鍵 · DELETE `/v1/projects/:projectId/keys/:keyId`で失効 |
| GET / PUT | `/v1/projects/:projectId/environments/:env/config` | マスターデータ |
| GET | `…/environments/:env/overview` | プレイヤー、日ごとのアクティブ数（14日分）、設定のバージョン、鍵 |
| GET | `…/environments/:env/players?q=&cursor=&limit=` · `…/players/:playerId` | 検索 / 詳細 |
| POST | `…/environments/:env/players/:playerId/ban` · `/unban` | BANすると接続中の通信をすぐに切断 |
| GET | `…/environments/:env/realtime` | 動いているルームの読み取り専用の表示 |
| GET | `/v1/projects/:projectId/audit?limit=&before=&env=` | 監査ログ |

ホスティングのエンドポイントも、開発者は`/v1/projects/:projectId/environments/:env/…`で使えます。

## お金（開発者セッション）

前払い残高と利用料です。開発者セッション（`Authorization: Bearer kds_…`）のみで、秘密鍵ではお金を読むことも動かすこともできません。金額は最小単位（セント）の整数です。

| メソッド | パス | 補足 |
|---|---|---|
| GET | `/v1/money/prepaid` · `/v1/money/prepaid/ledger` | `{ balance, owed, invite }` · 明細。`invite`は`{ granted, remaining, pending }`か`null`（`remaining`は`balance`の内数） |
| POST | `/v1/money/prepaid/checkout` | `{ amount ($5–$10,000), successUrl, cancelUrl }` + `Idempotency-Key` → Stripe Checkout（3Dセキュア）。状況はGET `/v1/money/prepaid/checkouts/:id`でポーリング |
| GET | `/v1/money/usage?period=YYYY-MM&refresh=` | 要素ごとの利用料（マイクロUSD）と、実際に請求した額（セント） |

## ゲームの運営（秘密鍵またはダッシュボード）

どれも、`X-Kumo-Key: sk_…`（自分のサーバー、CLI、MCP）なら`/v1/admin/<path>`で、開発者セッションなら`/v1/projects/:projectId/environments/:env/<path>`で使えます。変更は監査ログに記録されます。

| メソッド | `<path>` | 補足 |
|---|---|---|
| PUT | `players/:playerId/age` | `{ band }` — プレイヤーの年齢区分を訂正する（保護者の確認の後など） |
| POST | `players/:playerId/ban` · `players/:playerId/unban` | `{ reason?（500文字まで）, durationHours?（0より大きく8784まで。秘密鍵のみ。省くと解除するまで）}` → `{ player: { id, banned, bannedAt, banReason, revokedSessions, banExpiresAt } }`。接続中の通信をすぐに切断。404 `player_not_found`。機能のスイッチはなく常に使える。どこで掛けたBANもどこからでも解除できる |
| GET | `appeals?status=` · POST `appeals/:appealId/resolve` | プレイヤーからのBANへの異議申し立て · `{ accept, response? }` — 受け入れるとBANが解除される。いつでも使える |
| GET | `integrity/webhook-secret` | statsのwebhookが検証に使う鍵 |
| GET | `share/stats?from=&to=&shareId=&limit=` | 共有リンクごとの訪問、新規プレイヤー、プレイ数（UTCの日単位。既定は直近30日、最大366日） |
| POST | `share/links` | `{ label }` → 201 `{ link, url, query }` — 自分の投稿用の計測付きリンク |
| GET | `share/tags?ks=` | `/v1/share/tags`と同じ（`kumodeck share tags`が使う） |
