{
  "openapi": "3.1.0",
  "info": {
    "title": "TSUMU Client API",
    "version": "1.0.0",
    "description": "iPhone 2.2 / Web向け互換API。2026-09-22の実装に対応。管理・スキル作成・旧Android固有APIは対象外。TSUMUセッションはDiscordアクセストークンとは別物です。",
    "license": {
      "name": "この文書はTSUMUの現行クライアント契約を説明します"
    }
  },
  "servers": [
    {
      "url": "https://tsumu.tarohida.jp",
      "description": "既定接続先。実装するサーバーのHTTPSオリジンに置き換えてください。"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "sessionCookie": []
    }
  ],
  "paths": {
    "/auth/login": {
      "get": {
        "summary": "システムブラウザでDiscord認証を開始",
        "security": [],
        "description": "client=iosではcode_challengeとclient_stateが必須。challengeはS256固定、code_challenge_methodは送信しない。サーバーが独立したstateとoauth_state Cookieを発行しDiscordへ307リダイレクト。",
        "parameters": [
          {
            "name": "client",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "web",
                "ios",
                "android"
              ],
              "default": "web"
            }
          },
          {
            "name": "code_challenge",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "client_state",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{32,128}$"
            }
          }
        ],
        "responses": {
          "307": {
            "description": "Discord authorize URLへ。LocationとHttpOnly/Secure/SameSite=Laxのoauth_state Cookieを設定。Cookie有効期間300秒。"
          },
          "400": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/callback": {
      "get": {
        "summary": "Discordからのサーバー側コールバック",
        "security": [],
        "description": "oauth_state Cookieと保存済みstateを一回限り検証。iOS成功時はtsumu://auth?code=...&state=<client_state>へ302。認証拒否/Discordエラーもerrorとclient_stateを付けて302。state不正は/login.html?error=invalid_stateへ307。Web成功はsession Cookieを発行して/へ307。",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "iOSアプリへのワンタイムコードまたはerrorを含むLocation。"
          },
          "307": {
            "description": "Webトップまたはログインエラー画面へのLocation。"
          }
        }
      }
    },
    "/auth/token": {
      "post": {
        "summary": "iOSの一回限りコードをセッションに交換",
        "description": "JSON body。認可コードは60秒・一回限り。verifierとのS256一致を検証。expires_inは604800秒、実際の失効時刻はセッション作成から7日。refresh tokenはありません。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenExchange"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            }
          },
          "400": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/auth/logout": {
      "post": {
        "summary": "このセッションを失効",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "sessionCookie": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                },
                "example": {
                  "status": "ok"
                }
              }
            }
          }
        },
        "description": "現在のセッションを失効しCookieを削除。未認証でもok。"
      },
      "get": {
        "summary": "Web向けログアウト",
        "security": [
          {
            "sessionCookie": []
          },
          {}
        ],
        "responses": {
          "307": {
            "description": "/login.htmlへリダイレクト"
          }
        }
      }
    },
    "/api/game-data": {
      "get": {
        "summary": "ゲーム初期データ一括取得",
        "description": "iPhone起動時の必須JSON。認証付き・no-store。トップレベルの各キーを返す。サンプルは架空データで、sample.pngの画像自体は提供しない。",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GameData"
                },
                "example": {
                  "version": 1,
                  "user": {
                    "discord_id": "demo-user",
                    "username": "サンプル",
                    "jewels": 100,
                    "bestScore": 1200,
                    "gamesPlayed": 3
                  },
                  "tsums": {
                    "overrides": {
                      "mickey": {
                        "name": "サンプルツム",
                        "imageUrl": "/uploads/sample.png",
                        "skillId": "skill_example",
                        "skillName": "タイムプラス"
                      }
                    }
                  },
                  "settings": {
                    "combo_timeout": 3000,
                    "game_duration": 60,
                    "fever_duration": 8000,
                    "fever_multiplier": 3,
                    "min_chain": 3,
                    "bomb_chain_threshold": 7,
                    "connect_gap_tolerance": 26
                  },
                  "stage": null,
                  "fan": {
                    "url": null,
                    "color": "#4fc3f7"
                  },
                  "collection": {
                    "mickey": 2
                  },
                  "rankings": [
                    {
                      "name": "サンプル",
                      "score": 1200,
                      "tsumUsed": "mickey",
                      "createdAt": 1790000000
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "summary": "残高と自己ベスト等",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                },
                "example": {
                  "discord_id": "demo-user",
                  "username": "サンプル",
                  "jewels": 100,
                  "bestScore": 1200,
                  "gamesPlayed": 3
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/rankings": {
      "get": {
        "summary": "全ランキング（スコア降順・ページングなし）",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Rankings"
                },
                "example": [
                  {
                    "name": "サンプル",
                    "score": 1200,
                    "tsumUsed": "mickey",
                    "createdAt": 1790000000
                  }
                ]
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "スコア保存",
        "description": "request_idは省略可能だが再送安全性のためUUIDを必ず送る。同じユーザー+IDの再送は最初の保存を維持しok。同じIDで内容が変わっても更新しない。保存によるジュエル付与なし。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RankingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/gacha/pull": {
      "post": {
        "summary": "100ジュエルで1回ガチャ",
        "description": "残高と所持数を原子的に更新。同じユーザー+request_idは再消費せず同じtsumIdを返す。jewelsRemainingは再送時点の現在残高。",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GachaRequest"
              },
              "example": {
                "request_id": "11111111-1111-4111-8111-111111111111"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GachaResult"
                }
              }
            }
          },
          "400": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/user-tsums": {
      "get": {
        "summary": "ツムID別の所持数",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                },
                "example": {
                  "mickey": 2
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/skills/resolve": {
      "post": {
        "summary": "割り当て済みカスタムスキルのアクションを解決",
        "description": "activeのidはリクエスト内で一意。コード本体を受け取らず、myTsumIndexに割り当てたサーバースキルを実行する。現在の実装はCPU50ms・8MB・スタック256KBのQuickJS。再送冪等性は提供しない。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SkillBoard"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillResponse"
                }
              }
            }
          },
          "400": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/tsums": {
      "get": {
        "summary": "ツムの表示・スキル上書き（Web互換）",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TsumCatalog"
                },
                "example": {
                  "overrides": {
                    "mickey": {
                      "name": "サンプルツム",
                      "imageUrl": "/uploads/sample.png",
                      "skillId": "skill_example",
                      "skillName": "タイムプラス"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/settings": {
      "get": {
        "summary": "ゲーム設定（公開）",
        "security": [],
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                },
                "example": {
                  "combo_timeout": 3000,
                  "game_duration": 60,
                  "fever_duration": 8000,
                  "fever_multiplier": 3,
                  "min_chain": 3,
                  "bomb_chain_threshold": 7,
                  "connect_gap_tolerance": 26
                }
              }
            }
          }
        }
      }
    },
    "/api/stages/active": {
      "get": {
        "summary": "有効ステージ。未設定はnull（Web互換）",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stage"
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/fan-btn": {
      "get": {
        "summary": "シャッフル画像・色（Web互換）",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fan"
                },
                "example": {
                  "url": null,
                  "color": "#4fc3f7"
                }
              }
            }
          },
          "401": {
            "description": "エラー。通常detailは文字列、入力検証422では配列。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "TSUMUサーバーのセッショントークン。Discordのトークンではない。"
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "session",
        "description": "Web用HttpOnly Cookie。"
      }
    },
    "schemas": {
      "User": {
        "type": "object",
        "properties": {
          "discord_id": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "jewels": {
            "type": "integer"
          },
          "bestScore": {
            "type": "integer"
          },
          "gamesPlayed": {
            "type": "integer"
          }
        },
        "required": [
          "discord_id",
          "username",
          "jewels",
          "bestScore",
          "gamesPlayed"
        ]
      },
      "TsumOverride": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "skillId": {
            "type": [
              "string",
              "null"
            ]
          },
          "skillName": {
            "type": [
              "string",
              "null"
            ]
          },
          "skillCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "skillType": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": []
      },
      "TsumCatalog": {
        "type": "object",
        "properties": {
          "overrides": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/TsumOverride"
            }
          }
        },
        "required": [
          "overrides"
        ]
      },
      "Settings": {
        "type": "object",
        "properties": {
          "combo_timeout": {
            "type": "number",
            "default": 3000
          },
          "game_duration": {
            "type": "number",
            "default": 60
          },
          "fever_duration": {
            "type": "number",
            "default": 8000
          },
          "fever_multiplier": {
            "type": "number",
            "default": 3
          },
          "min_chain": {
            "type": "number",
            "default": 3
          },
          "bomb_chain_threshold": {
            "type": "number",
            "default": 7
          },
          "connect_gap_tolerance": {
            "type": "number",
            "default": 26
          }
        },
        "additionalProperties": {
          "type": "number"
        }
      },
      "Stage": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "bgUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "bgmUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "seconnectUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "seclearUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "sebombUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "seskillUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "sefeverUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "secomboUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "seresultUrl": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "Fan": {
        "type": "object",
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "color"
        ]
      },
      "Collection": {
        "type": "object",
        "additionalProperties": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Ranking": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "score": {
            "type": "integer"
          },
          "tsumUsed": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mickey",
              "donald",
              "goofy",
              "pluto",
              "daisy",
              "minnie",
              null
            ]
          },
          "createdAt": {
            "type": "number",
            "description": "UTC Unix timestamp in seconds"
          }
        },
        "required": [
          "name",
          "score",
          "createdAt"
        ]
      },
      "Rankings": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Ranking"
        }
      },
      "GameData": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer",
            "const": 1
          },
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "tsums": {
            "$ref": "#/components/schemas/TsumCatalog"
          },
          "settings": {
            "$ref": "#/components/schemas/Settings"
          },
          "stage": {
            "$ref": "#/components/schemas/Stage"
          },
          "fan": {
            "$ref": "#/components/schemas/Fan"
          },
          "collection": {
            "$ref": "#/components/schemas/Collection"
          },
          "rankings": {
            "$ref": "#/components/schemas/Rankings"
          }
        },
        "required": [
          "version",
          "user",
          "tsums",
          "settings",
          "stage",
          "fan",
          "collection",
          "rankings"
        ]
      },
      "TokenExchange": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "minLength": 20,
            "maxLength": 128
          },
          "code_verifier": {
            "type": "string",
            "minLength": 43,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9._~-]+$"
          }
        },
        "required": [
          "code",
          "code_verifier"
        ]
      },
      "Token": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "const": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "const": 604800
          }
        },
        "required": [
          "access_token",
          "token_type",
          "expires_in"
        ]
      },
      "RankingRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20
          },
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647
          },
          "tsum_used": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mickey",
              "donald",
              "goofy",
              "pluto",
              "daisy",
              "minnie",
              null
            ]
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "required": [
          "name",
          "score"
        ]
      },
      "GachaRequest": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "required": []
      },
      "GachaResult": {
        "type": "object",
        "properties": {
          "tsumId": {
            "type": "string",
            "enum": [
              "mickey",
              "donald",
              "goofy",
              "pluto",
              "daisy",
              "minnie"
            ]
          },
          "jewelsRemaining": {
            "type": "integer"
          }
        },
        "required": [
          "tsumId",
          "jewelsRemaining"
        ]
      },
      "Status": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          }
        },
        "required": [
          "status"
        ]
      },
      "BoardPosition": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number",
            "minimum": -1000,
            "maximum": 2000
          },
          "y": {
            "type": "number",
            "minimum": -1000,
            "maximum": 2000
          }
        },
        "required": [
          "x",
          "y"
        ]
      },
      "BoardTsum": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "body": {
            "$ref": "#/components/schemas/BoardPosition"
          },
          "typeIndex": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5
          }
        },
        "required": [
          "id",
          "body",
          "typeIndex"
        ]
      },
      "SkillBoard": {
        "type": "object",
        "properties": {
          "myTsumIndex": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5
          },
          "active": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/BoardTsum"
            }
          }
        },
        "required": [
          "myTsumIndex",
          "active"
        ]
      },
      "SkillAction": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "enum": [
                  "clear",
                  "transform"
                ]
              },
              "ids": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "type",
              "ids"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "time"
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "maximum": 3600
              }
            },
            "required": [
              "type",
              "value"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "score"
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "maximum": 1000000
              }
            },
            "required": [
              "type",
              "value"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "boost"
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "maximum": 3600000
              }
            },
            "required": [
              "type",
              "value"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "bomb"
              },
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 375
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 1000
              }
            },
            "required": [
              "type",
              "x",
              "y"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "fever"
              }
            },
            "required": [
              "type"
            ]
          }
        ]
      },
      "SkillResponse": {
        "type": "object",
        "properties": {
          "actions": {
            "type": "array",
            "maxItems": 64,
            "items": {
              "$ref": "#/components/schemas/SkillAction"
            }
          }
        },
        "required": [
          "actions"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "detail": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            ]
          }
        },
        "required": [
          "detail"
        ]
      }
    }
  }
}
