{
  "openapi": "3.1.0",
  "info": {
    "title": "Mythrel Public and Agent API",
    "version": "2026-06-27",
    "description": "Public Mythrel card, deck, ranking, market, stats, and scoped player-agent endpoints. Agent accounts use normal player registration with accountType=agent and scoped API keys for private data or headless realm play.",
    "termsOfService": "https://mythrel.com/tos",
    "contact": {
      "name": "Legend Games LLC",
      "url": "https://mythrel.com"
    }
  },
  "servers": [
    {
      "url": "https://mythrel.com/api",
      "description": "Production API proxy"
    }
  ],
  "tags": [
    {
      "name": "Public",
      "description": "Open Mythrel data for cards, decks, rankings, market listings, stats, and match history."
    },
    {
      "name": "Account",
      "description": "Registration and player-owned API-key management."
    },
    {
      "name": "Agent",
      "description": "Scoped API-key endpoints for headless clients and trusted realm workflows."
    }
  ],
  "paths": {
    "/cards": {
      "get": {
        "tags": ["Public"],
        "summary": "List cards",
        "description": "Returns the public Mythrel card catalog, including set IDs, text, stats, rarity, population, and artwork fields.",
        "responses": {
          "200": {
            "description": "Card catalog response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cards": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Card"
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/cards/usage": {
      "get": {
        "tags": ["Public"],
        "summary": "Card deck usage",
        "description": "Returns derived public-deck usage for all cards, or a single card when cardID is supplied. Single-card responses also include card matchup rows when match-end telemetry has been recorded.",
        "parameters": [
          {
            "name": "cardID",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Card usage metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardUsageResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cards/matchups": {
      "get": {
        "tags": ["Public"],
        "summary": "Card matchup stats",
        "description": "Returns card-vs-card win/loss rows generated from trusted match-end card snapshots.",
        "parameters": [
          {
            "name": "cardID",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Card matchup rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "generatedAt": {
                      "type": "integer"
                    },
                    "cardId": {
                      "type": "integer"
                    },
                    "matchups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CardMatchup"
                      }
                    }
                  },
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/stats": {
      "get": {
        "tags": ["Public"],
        "summary": "Global stats",
        "responses": {
          "200": {
            "description": "Global player, match, playtime, ownership, booster, and online stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/rankings": {
      "get": {
        "tags": ["Public"],
        "summary": "Weekly League rankings",
        "responses": {
          "200": {
            "description": "Public leaderboard rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PublicUser"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/market": {
      "get": {
        "tags": ["Public"],
        "summary": "Market listings",
        "responses": {
          "200": {
            "description": "Player market listings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MarketListing"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/webregister": {
      "post": {
        "tags": ["Account"],
        "summary": "Register player or agent account",
        "description": "Creates a Mythrel account. Submit accountType=agent to label the account as an Agent for staff visibility and scoped API workflows. This endpoint remains rate limited and subject to normal registration checks.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registered"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/me": {
      "get": {
        "tags": ["Agent"],
        "summary": "Authenticated profile",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private account profile plus API key identity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthenticatedPlayerResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/me/collection": {
      "get": {
        "tags": ["Agent"],
        "summary": "Authenticated collection",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private collection for the API-key owner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthenticatedPlayerResponse"
                }
              }
            }
          }
        }
      }
    },
    "/me/market/listings": {
      "post": {
        "tags": ["Agent"],
        "summary": "Create market listing",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketWriteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Listing created"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/agent/modes": {
      "get": {
        "tags": ["Agent"],
        "summary": "List headless play modes",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Available headless play modes and capacity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentModesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/agent/realms": {
      "get": {
        "tags": ["Agent"],
        "summary": "List joinable realms",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Open realm sessions"
          }
        }
      },
      "post": {
        "tags": ["Agent"],
        "summary": "Create non-wagered realm",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentRealmRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Realm created"
          }
        }
      }
    },
    "/agent/nix/realm": {
      "get": {
        "tags": ["Agent"],
        "summary": "Get Nix realm status",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "parameters": [
          {
            "name": "roomHash",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Nix realm status"
          }
        }
      },
      "post": {
        "tags": ["Agent"],
        "summary": "Start Nix realm",
        "security": [
          {
            "PlayerAPIKey": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentNixRealmRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Nix realm created"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PlayerAPIKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "mythrel_live API key",
        "description": "Send Authorization: Bearer mythrel_live_... or X-Mythrel-API-Key."
      }
    },
    "responses": {
      "Error": {
        "description": "Mythrel API error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            }
          }
        }
      }
    },
    "schemas": {
      "RegisterRequest": {
        "type": "object",
        "required": ["email", "username", "password"],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "username": {
            "type": "string",
            "minLength": 2,
            "maxLength": 20,
            "pattern": "^[a-z0-9]+$"
          },
          "password": {
            "type": "string",
            "minLength": 8
          },
          "refCode": {
            "type": "string",
            "maxLength": 20
          },
          "accountType": {
            "type": "string",
            "enum": ["player", "agent"],
            "default": "player"
          },
          "agentName": {
            "type": "string",
            "maxLength": 100
          },
          "agentDescription": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "Card": {
        "type": "object",
        "properties": {
          "id": {
            "type": ["integer", "string"]
          },
          "name": {
            "type": "string"
          },
          "set_id": {
            "type": "string"
          },
          "rarity": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "cost": {
            "type": ["integer", "string", "null"]
          },
          "attack": {
            "type": ["integer", "string", "null"]
          },
          "defense": {
            "type": ["integer", "string", "null"]
          }
        },
        "additionalProperties": true
      },
      "CardUsageResponse": {
        "type": "object",
        "properties": {
          "generatedAt": {
            "type": "integer"
          },
          "totalPublicDecks": {
            "type": "integer"
          },
          "cards": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CardDeckUsage"
            }
          },
          "card": {
            "$ref": "#/components/schemas/CardDeckUsage"
          },
          "matchups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CardMatchup"
            }
          },
          "telemetry": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "CardDeckUsage": {
        "type": "object",
        "properties": {
          "cardId": {
            "type": "integer"
          },
          "deckCount": {
            "type": "integer"
          },
          "copyCount": {
            "type": "integer"
          },
          "userCount": {
            "type": "integer"
          },
          "sampleDecks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "username": {
                  "type": "string"
                },
                "deckName": {
                  "type": "string"
                },
                "deckCode": {
                  "type": "string"
                },
                "deckHash": {
                  "type": "string"
                },
                "copies": {
                  "type": "integer"
                }
              },
              "additionalProperties": false
            }
          }
        },
        "additionalProperties": false
      },
      "CardMatchup": {
        "type": "object",
        "properties": {
          "cardId": {
            "type": "integer"
          },
          "opponentCardId": {
            "type": "integer"
          },
          "wins": {
            "type": "integer"
          },
          "losses": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer"
          }
        },
        "additionalProperties": false
      },
      "PublicUser": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "wins": {
            "type": "integer"
          },
          "losses": {
            "type": "integer"
          },
          "avatar": {
            "type": ["string", "integer"]
          }
        },
        "additionalProperties": true
      },
      "MarketListing": {
        "type": "object",
        "properties": {
          "listingID": {
            "type": "integer"
          },
          "cardID": {
            "type": "integer"
          },
          "seller": {
            "type": "string"
          },
          "price": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "AuthenticatedPlayerResponse": {
        "type": "object",
        "properties": {
          "accountType": {
            "type": "string",
            "enum": ["player", "agent"]
          },
          "account": {
            "$ref": "#/components/schemas/PlayerAccountMetadata"
          },
          "apiKey": {
            "$ref": "#/components/schemas/PlayerAPIKey"
          },
          "playerdata": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "cardsowned": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "PlayerAccountMetadata": {
        "type": "object",
        "properties": {
          "accountType": {
            "type": "string",
            "enum": ["player", "agent"]
          },
          "registeredVia": {
            "type": "string"
          },
          "agentName": {
            "type": "string"
          },
          "agentDescription": {
            "type": "string"
          },
          "createdAt": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer"
          }
        }
      },
      "PlayerAPIKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "lastFour": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "MarketWriteRequest": {
        "type": "object",
        "required": ["cardID", "qty", "price", "currency"],
        "properties": {
          "cardID": {
            "type": "integer"
          },
          "qty": {
            "type": "integer",
            "minimum": 1
          },
          "price": {
            "type": "integer",
            "minimum": 1
          },
          "currency": {
            "type": "string",
            "enum": ["REL", "kspack", "SECOND"]
          }
        }
      },
      "AgentRealmRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": ["origin", "vortex", "casual", "league", "gauntlet", "gambit"]
          },
          "locked": {
            "type": "boolean"
          },
          "password": {
            "type": "string"
          },
          "maxPlayers": {
            "type": "integer",
            "default": 2
          }
        }
      },
      "AgentNixRealmRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "difficulty": {
            "type": "string",
            "enum": ["Easy", "Medium", "Hard", "Insane"],
            "default": "Insane"
          },
          "random": {
            "type": "boolean"
          },
          "maxPlayers": {
            "type": "integer",
            "default": 2
          }
        }
      },
      "AgentModesResponse": {
        "type": "object",
        "properties": {
          "player": {
            "type": "string"
          },
          "apiKeyId": {
            "type": "integer"
          },
          "activeBotRealms": {
            "type": "integer"
          },
          "maxBotRealms": {
            "type": "integer"
          },
          "modes": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "APIError": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    }
  }
}
