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_… |
エラーは常に次の形です。
{ "error": { "code": "version_conflict", "message": "…", "details": { "currentVersion": 7 } } }APIでは、アプリを使う人(利用者)を players(プレイヤー。/v1/players/…、playerId)と呼びます。このページもその名前のまま書きます。
どのオリジンからでも。 SDKは、どこでホストしたアプリやゲーム(KUMODeckのホスティング、自分のサーバー、itch.io、localhost)からでも動きます。CORSは開放されていて、認証はヘッダーで行います。ほかのサイトに自分の公開鍵を使わせないようにするには、kumo.config.json → web.allowedOriginsに自分のオリジンを列挙します(設定を参照)。以後、ほかのオリジンからのブラウザのリクエストは403 origin_not_allowedになります。どのモジュールも単独で動きます。セーブだけ、マルチプレイだけ、ホスティングだけ、といった使い方ができます。
機能はONにするまでOFFです(設定の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を返します。ログイン・認証を参照してください。
ゲームデータ(プレイヤー)#
| メソッド | パス | 補足 |
|---|---|---|
| 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です(ガイド)。同じ/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 — 音声通話。これと通話のほかのメッセージ(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です(設定とガイドを参照)。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のルートを使います(ログイン・認証を参照)。ダッシュボードに自分の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(秘密鍵またはダッシュボード)#
自分のサーバーのプログラムとデータベースです(ガイド)。既定は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が使う) |