{
  "openapi": "3.0.3",
  "info": {
    "title": "Hotaku API",
    "version": "1.0.0",
    "description": "REST API for Hotaku — decentralized prediction markets for eSports (Counter-Strike 2, League of Legends, Dota 2, Valorant, etc.), settled in USDC on Solana via LMSR (Logarithmic Market Scoring Rule).\n\nSee https://hotaku.fun/llms.txt for an agent-oriented overview.\n\n**Auth model:**\n- Public read endpoints require no auth.\n- Trading and balance endpoints require a per-wallet API key obtained via `GET /auth/challenge` + `POST /auth/create-api-key`. Pass it as `Authorization: Bearer <api_key>`.\n- Admin endpoints (market creation/resolution) require the `x-api-key` header set to the operator's admin key.",
    "contact": {
      "name": "Hotaku",
      "url": "https://hotaku.fun"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.hotaku.fun",
      "description": "Production API"
    }
  ],
  "tags": [
    { "name": "markets", "description": "Public market data and order placement (on-chain flow)" },
    { "name": "positions", "description": "User positions across markets" },
    { "name": "trading", "description": "Authenticated bot/agent trading using internal balance" },
    { "name": "balance", "description": "Internal balance management for authenticated wallets" },
    { "name": "auth", "description": "Wallet-signature authentication for API keys" },
    { "name": "profiles", "description": "User profile metadata" },
    { "name": "comments", "description": "Per-market discussion threads" },
    { "name": "teams", "description": "eSports teams referenced by markets" },
    { "name": "faqs", "description": "Static FAQ entries" }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Per-wallet API key from `POST /auth/create-api-key`."
      },
      "adminApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Admin operator key (market creation, resolution)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string" }
        }
      },
      "Market": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "question": { "type": "string" },
          "game": { "type": "string", "description": "eSports game (e.g., cs2, lol, dota2, valorant)" },
          "team1": { "type": "string" },
          "team2": { "type": "string" },
          "team1_short": { "type": "string", "nullable": true },
          "team2_short": { "type": "string", "nullable": true },
          "team1_color": { "type": "string" },
          "team2_color": { "type": "string" },
          "team1_logo": { "type": "string", "nullable": true },
          "team2_logo": { "type": "string", "nullable": true },
          "tournament": { "type": "string", "nullable": true },
          "match_time": { "type": "string", "format": "date-time", "nullable": true },
          "best_of": { "type": "integer" },
          "q1": { "type": "number", "description": "LMSR shares outstanding for team1" },
          "q2": { "type": "number", "description": "LMSR shares outstanding for team2" },
          "b": { "type": "number", "description": "LMSR liquidity parameter" },
          "liquidity": { "type": "number" },
          "volume": { "type": "number" },
          "fee_bps": { "type": "integer", "description": "Trading fee in basis points (50 = 0.5%)" },
          "live": { "type": "boolean" },
          "resolved": { "type": "boolean" },
          "outcome": { "type": "string", "enum": ["team1", "team2"], "nullable": true },
          "end_date": { "type": "string", "format": "date-time", "nullable": true },
          "team1_price": { "type": "number", "description": "Implied probability of team1 (0..1)" },
          "team2_price": { "type": "number", "description": "Implied probability of team2 (0..1)" }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "market_id": { "type": "string" },
          "user_address": { "type": "string" },
          "side": { "type": "string", "enum": ["team1", "team2"] },
          "action": { "type": "string", "enum": ["buy", "sell"] },
          "shares": { "type": "number" },
          "cost_usdc": { "type": "number" },
          "fee_usdc": { "type": "number" },
          "price_at_execution": { "type": "number" },
          "tx_hash": { "type": "string", "nullable": true },
          "chain": { "type": "string", "default": "solana" }
        }
      },
      "Position": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "market_id": { "type": "string" },
          "user_address": { "type": "string" },
          "team1_shares": { "type": "number" },
          "team2_shares": { "type": "number" },
          "total_cost": { "type": "number" },
          "current_value": { "type": "number" },
          "pnl": { "type": "number" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "Quote": {
        "type": "object",
        "properties": {
          "market_id": { "type": "string" },
          "side": { "type": "string", "enum": ["team1", "team2"] },
          "action": { "type": "string", "enum": ["buy", "sell"] },
          "shares": { "type": "number" },
          "cost_usdc": { "type": "number" },
          "payout_usdc": { "type": "number" },
          "fee_usdc": { "type": "number" },
          "price": { "type": "number" },
          "prices": {
            "type": "object",
            "properties": {
              "team1": { "type": "number" },
              "team2": { "type": "number" }
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credentials",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid input",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    }
  },
  "paths": {
    "/": {
      "get": {
        "tags": ["markets"],
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "Service is up",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string" }, "service": { "type": "string" } } } } }
          }
        }
      }
    },
    "/markets": {
      "get": {
        "tags": ["markets"],
        "summary": "List markets",
        "parameters": [
          { "name": "game", "in": "query", "schema": { "type": "string" } },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["active", "resolved", "live"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": {
          "200": {
            "description": "List of markets with implied prices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "markets": { "type": "array", "items": { "$ref": "#/components/schemas/Market" } }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["markets"],
        "summary": "Create market (admin)",
        "security": [{ "adminApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["question", "game", "team1", "team2"],
                "properties": {
                  "id": { "type": "string" },
                  "question": { "type": "string" },
                  "game": { "type": "string" },
                  "team1": { "type": "string" },
                  "team2": { "type": "string" },
                  "team1_short": { "type": "string" },
                  "team2_short": { "type": "string" },
                  "team1_color": { "type": "string" },
                  "team2_color": { "type": "string" },
                  "team1_logo": { "type": "string" },
                  "team2_logo": { "type": "string" },
                  "tournament": { "type": "string" },
                  "match_time": { "type": "string", "format": "date-time" },
                  "best_of": { "type": "integer" },
                  "b": { "type": "number" },
                  "liquidity": { "type": "number" },
                  "end_date": { "type": "string", "format": "date-time" },
                  "live": { "type": "boolean" },
                  "fee_bps": { "type": "integer" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Market created", "content": { "application/json": { "schema": { "type": "object", "properties": { "market": { "$ref": "#/components/schemas/Market" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/markets/{id}": {
      "get": {
        "tags": ["markets"],
        "summary": "Get a single market",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Market", "content": { "application/json": { "schema": { "type": "object", "properties": { "market": { "$ref": "#/components/schemas/Market" } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/markets/{id}/order": {
      "post": {
        "tags": ["markets"],
        "summary": "Place a buy/sell order (on-chain flow)",
        "description": "Buy orders require an on-chain `tx_hash` proving the USDC transfer to the vault. Sell orders may return a `claim_auth` payload (ed25519 signature) the caller submits to the Solana program to withdraw.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["side", "action", "amount", "user_address"],
                "properties": {
                  "side": { "type": "string", "enum": ["team1", "team2"] },
                  "action": { "type": "string", "enum": ["buy", "sell"] },
                  "amount": { "type": "number", "description": "USDC for buys, shares for sells" },
                  "user_address": { "type": "string", "description": "Solana base58 or EVM 0x address" },
                  "tx_hash": { "type": "string", "description": "Required for buys" },
                  "chain": { "type": "string", "default": "solana" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Order accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": { "$ref": "#/components/schemas/Order" },
                    "prices": { "type": "object", "properties": { "team1": { "type": "number" }, "team2": { "type": "number" } } },
                    "claim_auth": { "type": "object", "description": "Present on sell orders; signature payload for the Solana program" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/markets/{id}/order-tx": {
      "patch": {
        "tags": ["markets"],
        "summary": "Attach an on-chain tx_hash to a pending sell order",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["user_address", "tx_hash"],
                "properties": {
                  "user_address": { "type": "string" },
                  "tx_hash": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/markets/{id}/orderbook": {
      "get": {
        "tags": ["markets"],
        "summary": "Recent orders for a market",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": {
          "200": {
            "description": "Recent orders",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "orders": { "type": "array", "items": { "$ref": "#/components/schemas/Order" } } } } } }
          }
        }
      }
    },
    "/markets/{id}/holders": {
      "get": {
        "tags": ["markets"],
        "summary": "Top holders for a market",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } }
        ],
        "responses": {
          "200": {
            "description": "Holders",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "holders": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "user_address": { "type": "string" },
                          "display_name": { "type": "string", "nullable": true },
                          "team1_shares": { "type": "number" },
                          "team2_shares": { "type": "number" },
                          "total_cost": { "type": "number" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/markets/{id}/resolve": {
      "post": {
        "tags": ["markets"],
        "summary": "Resolve a market (admin)",
        "security": [{ "adminApiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["outcome"],
                "properties": { "outcome": { "type": "string", "enum": ["team1", "team2"] } }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Resolved", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "market_id": { "type": "string" }, "outcome": { "type": "string" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/markets/{id}/claim": {
      "post": {
        "tags": ["markets"],
        "summary": "Claim winnings on a resolved market",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["user_address"],
                "properties": { "user_address": { "type": "string" } }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Claim accepted; each winning share pays $1.00 USDC",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "claim": {
                      "type": "object",
                      "properties": {
                        "id": { "type": "string" },
                        "market_id": { "type": "string" },
                        "user_address": { "type": "string" },
                        "payout_usdc": { "type": "number" }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/positions/{address}": {
      "get": {
        "tags": ["positions"],
        "summary": "All positions for a wallet (with current value & PnL)",
        "parameters": [{ "name": "address", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": {
            "description": "Positions",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "positions": { "type": "array", "items": { "$ref": "#/components/schemas/Position" } } } } } }
          }
        }
      }
    },
    "/positions/{address}/orders": {
      "get": {
        "tags": ["positions"],
        "summary": "Cumulative PnL series from order history",
        "parameters": [{ "name": "address", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": {
            "description": "Time series",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": { "type": "string", "format": "date-time" },
                          "pnl": { "type": "number" },
                          "action": { "type": "string", "enum": ["buy", "sell"] },
                          "market_id": { "type": "string" },
                          "amount": { "type": "number" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/challenge": {
      "get": {
        "tags": ["auth"],
        "summary": "Get a nonce to sign for API key creation",
        "parameters": [{ "name": "address", "in": "query", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Nonce", "content": { "application/json": { "schema": { "type": "object", "properties": { "nonce": { "type": "string" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/auth/create-api-key": {
      "post": {
        "tags": ["auth"],
        "summary": "Exchange a signed nonce for a 30-day API key",
        "description": "Sign the literal message `Hotaku API Key\\nWallet: <address>\\nNonce: <nonce>` with the wallet's ed25519 key (Solana). Pass the base58 signature.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["address", "signature"],
                "properties": {
                  "address": { "type": "string" },
                  "signature": { "type": "string", "description": "base58-encoded ed25519 signature" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "API key issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api_key": { "type": "string" },
                    "address": { "type": "string" },
                    "expires_at": { "type": "integer" },
                    "expires_in": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/balance": {
      "get": {
        "tags": ["balance"],
        "summary": "Get current internal balance",
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_address": { "type": "string" },
                    "available": { "type": "number" },
                    "locked": { "type": "number" },
                    "total": { "type": "number" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/balance/deposit": {
      "post": {
        "tags": ["balance"],
        "summary": "Register an on-chain USDC deposit",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["tx_hash", "amount"],
                "properties": {
                  "tx_hash": { "type": "string" },
                  "amount": { "type": "number" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Deposit credited" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "description": "tx_hash already registered" }
        }
      }
    },
    "/balance/withdraw": {
      "post": {
        "tags": ["balance"],
        "summary": "Withdraw from internal balance to wallet",
        "description": "Returns a `claim_auth` payload (ed25519-signed) that the caller submits to the Solana program to release USDC.",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amount"],
                "properties": { "amount": { "type": "number" } }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Claim authorization" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/balance/history": {
      "get": {
        "tags": ["balance"],
        "summary": "Transaction history",
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Transactions" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/trading/quote": {
      "get": {
        "tags": ["trading"],
        "summary": "LMSR quote for a hypothetical buy or sell (no auth)",
        "parameters": [
          { "name": "market_id", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "side", "in": "query", "required": true, "schema": { "type": "string", "enum": ["team1", "team2"] } },
          { "name": "amount", "in": "query", "required": true, "schema": { "type": "number" }, "description": "USDC for buys, shares for sells" },
          { "name": "action", "in": "query", "schema": { "type": "string", "enum": ["buy", "sell"], "default": "buy" } }
        ],
        "responses": {
          "200": { "description": "Quote", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Quote" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/trading/buy": {
      "post": {
        "tags": ["trading"],
        "summary": "Execute a buy against internal balance",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["market_id", "side", "amount"],
                "properties": {
                  "market_id": { "type": "string" },
                  "side": { "type": "string", "enum": ["team1", "team2"] },
                  "amount": { "type": "number", "description": "USDC to spend" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Order executed", "content": { "application/json": { "schema": { "type": "object", "properties": { "order": { "$ref": "#/components/schemas/Order" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/trading/sell": {
      "post": {
        "tags": ["trading"],
        "summary": "Execute a sell against internal balance",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["market_id", "side", "shares"],
                "properties": {
                  "market_id": { "type": "string" },
                  "side": { "type": "string", "enum": ["team1", "team2"] },
                  "shares": { "type": "number" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Order executed" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/trading/positions": {
      "get": {
        "tags": ["trading"],
        "summary": "Authenticated user's positions",
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Positions",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "positions": { "type": "array", "items": { "$ref": "#/components/schemas/Position" } } } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/profiles/{address}": {
      "get": {
        "tags": ["profiles"],
        "summary": "Get profile for a wallet",
        "parameters": [{ "name": "address", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Profile (or null)" }
        }
      }
    },
    "/profiles": {
      "post": {
        "tags": ["profiles"],
        "summary": "Create or update a profile",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["user_address"],
                "properties": {
                  "user_address": { "type": "string" },
                  "display_name": { "type": "string" },
                  "referral_code_used": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Profile upserted" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "409": { "description": "Display name already taken" }
        }
      }
    },
    "/profiles/referral/{code}": {
      "get": {
        "tags": ["profiles"],
        "summary": "Look up a profile by referral code",
        "parameters": [{ "name": "code", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Referrer profile (or null)" }
        }
      }
    },
    "/profiles/check-name/{name}": {
      "get": {
        "tags": ["profiles"],
        "summary": "Check whether a display name is available",
        "parameters": [{ "name": "name", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Availability flag" }
        }
      }
    },
    "/comments": {
      "get": {
        "tags": ["comments"],
        "summary": "List comments (filtered by market)",
        "parameters": [
          { "name": "market_id", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } }
        ],
        "responses": { "200": { "description": "Comments" } }
      },
      "post": {
        "tags": ["comments"],
        "summary": "Post a comment",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["market_id", "author_address", "content"],
                "properties": {
                  "market_id": { "type": "string" },
                  "author_address": { "type": "string" },
                  "content": { "type": "string" },
                  "parent_id": { "type": "string", "nullable": true }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Created" },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/comments/{id}/replies": {
      "get": {
        "tags": ["comments"],
        "summary": "Replies to a comment",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Replies" } }
      }
    },
    "/comments/{id}/like": {
      "post": {
        "tags": ["comments"],
        "summary": "Like (or unlike) a comment",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Like state" } }
      }
    },
    "/teams": {
      "get": {
        "tags": ["teams"],
        "summary": "List eSports teams referenced by markets",
        "responses": { "200": { "description": "Teams" } }
      }
    },
    "/teams/{id}": {
      "get": {
        "tags": ["teams"],
        "summary": "Get a team",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Team" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/faqs": {
      "get": {
        "tags": ["faqs"],
        "summary": "List FAQs",
        "responses": { "200": { "description": "FAQ entries" } }
      }
    }
  }
}
