{
  "openapi": "3.0.0",
  "info": {
    "title": "Runic Gateway API",
    "version": "1.0.0",
    "description": "REST API for the Runic Gateway website, wiki and admin panel — a private Ultima Online shard.\n\n### Authentication\n- **Web / admin panel** uses an httpOnly session cookie (`rg_token`) issued by `POST /api/v1/auth/login` (plus `/login/totp` when 2FA is enabled).\n- **Native / mobile clients** use bearer access tokens from `POST /api/v1/auth/mobile/login`, refreshed via `/auth/mobile/refresh`.\n\nEndpoints under `/api/v1/admin/**` require a valid session; some are further restricted to the `admin` role (editors are limited to content)."
  },
  "servers": [
    {
      "url": "/",
      "description": "Same-origin (current host)"
    },
    {
      "url": "http://localhost:3000",
      "description": "Local development"
    }
  ],
  "tags": [
    {
      "name": "Health",
      "description": "Liveness probe"
    },
    {
      "name": "Auth",
      "description": "Web session login/logout (cookie + TOTP)"
    },
    {
      "name": "Auth · Mobile",
      "description": "Native bearer-token login, refresh and logout"
    },
    {
      "name": "Auth · SSO",
      "description": "OAuth2 / OIDC provider discovery and redirect flow"
    },
    {
      "name": "Public",
      "description": "Unauthenticated site content (settings, posts, wiki, contact)"
    },
    {
      "name": "Public · Shard",
      "description": "Live shard data ingested from the uo-link sidecar (status, feed, economy, IDOC, characters)"
    },
    {
      "name": "Public · Atlas",
      "description": "Spawn atlas / bestiary — static shard content parsed from the shard's own ServUO tree, independent of the sidecar"
    },
    {
      "name": "Admin · Account",
      "description": "Self-service account security (2FA, linked identities)"
    },
    {
      "name": "Player",
      "description": "Self-service player accounts (register, credentials, 2FA, linked identities)"
    },
    {
      "name": "Player · Shard",
      "description": "Link an in-game account and read its roster / vendors (uo-link)"
    },
    {
      "name": "Player · Appeals",
      "description": "Player-submitted moderation appeals"
    },
    {
      "name": "Settings",
      "description": "Site-wide settings any authenticated account may read (nav overrides)"
    },
    {
      "name": "Admin · Dashboard",
      "description": "Dashboard summary and site mode"
    },
    {
      "name": "Admin · Posts",
      "description": "News / five-on-friday / newsletter / screenshots + uploads"
    },
    {
      "name": "Admin · Wiki",
      "description": "Wiki pages, categories, tags and revisions"
    },
    {
      "name": "Admin · Settings",
      "description": "Site settings (admin only)"
    },
    {
      "name": "Admin · Activity",
      "description": "Admin activity log"
    },
    {
      "name": "Admin · Bot Activity",
      "description": "Bot-scoring/ban state and emergency unban (admin only)"
    },
    {
      "name": "Admin · Discord Bot",
      "description": "Discord bot token/config and live status (admin only)"
    },
    {
      "name": "Admin · Shard",
      "description": "uo-link sidecar connection config, live status and town crier (admin only)"
    },
    {
      "name": "Admin · Auth Providers",
      "description": "SSO provider configuration (admin only)"
    },
    {
      "name": "Admin · Users",
      "description": "User management (admin only)"
    }
  ],
  "paths": {
    "/api/csp-report": {
      "post": {
        "tags": [
          "Health"
        ],
        "summary": "Content-Security-Policy violation report sink",
        "description": "Receives CSP violation reports from browsers (both the `report-uri` `application/csp-report` format and the Reporting API `application/reports+json` format). Unauthenticated by necessity — browsers send reports with no session. Reports are logged, never stored or echoed. Always answers 204.",
        "responses": {
          "204": {
            "description": "Report accepted (or ignored). No content."
          },
          "429": {
            "description": "Too many reports from this address."
          }
        },
        "security": []
      }
    },
    "/api/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Liveness probe",
        "description": "",
        "responses": {
          "200": {
            "description": "Service is up",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/account": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Get the current account (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "The account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountStatus"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/account/identities": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "List linked SSO identities (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Linked identities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LinkedIdentity"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/account/identities/{provider}": {
      "delete": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Unlink an SSO identity (self)",
        "description": "",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id."
          }
        ],
        "responses": {
          "200": {
            "description": "Unlinked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnlinkedFlag"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No linked account for that provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/account/totp/disable": {
      "post": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Disable 2FA by confirming a code",
        "description": "",
        "responses": {
          "200": {
            "description": "2FA disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpState"
                }
              }
            }
          },
          "400": {
            "description": "Not enabled, or invalid code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpCodeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/account/totp/enable": {
      "post": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Enable 2FA by confirming a code",
        "description": "",
        "responses": {
          "200": {
            "description": "2FA enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpState"
                }
              }
            }
          },
          "400": {
            "description": "Setup not started, or invalid code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Two-factor already enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpCodeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/account/totp/setup": {
      "post": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Begin 2FA enrollment (returns secret + QR)",
        "description": "",
        "responses": {
          "200": {
            "description": "otpauth URL and QR data to scan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpSetup"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Two-factor already enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/activity": {
      "get": {
        "tags": [
          "Admin · Activity"
        ],
        "summary": "List recent admin activity",
        "description": "",
        "parameters": [
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Max rows to return."
          }
        ],
        "responses": {
          "200": {
            "description": "Activity entries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/auth/providers": {
      "get": {
        "tags": [
          "Admin · Auth Providers"
        ],
        "summary": "List configured SSO providers (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Providers (secrets stripped)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProviderConfig"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Auth Providers"
        ],
        "summary": "Create a custom SSO provider (admin only)",
        "description": "Built-in providers (google, discord) are configured via PUT, not created here.",
        "responses": {
          "201": {
            "description": "Created provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderConfig"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or a built-in/invalid kind",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Provider id already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProviderCreateRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/auth/providers/{id}": {
      "put": {
        "tags": [
          "Admin · Auth Providers"
        ],
        "summary": "Update an SSO provider (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderConfig"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Provider not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProviderCreateRequest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin · Auth Providers"
        ],
        "summary": "Delete a custom SSO provider (admin only)",
        "description": "Built-in providers cannot be deleted — disable them instead.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedFlag"
                }
              }
            }
          },
          "400": {
            "description": "Built-in provider cannot be deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Provider not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/bot-activity": {
      "get": {
        "tags": [
          "Admin · Bot Activity"
        ],
        "summary": "Bot-scoring / ban state and recent events (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Banned IPs, scores and recent events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/bot-activity/unban": {
      "post": {
        "tags": [
          "Admin · Bot Activity"
        ],
        "summary": "Emergency unban an IP (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Unbanned (echoes the ip and whether an entry was cleared)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnbanResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid IP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnbanRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/dashboard": {
      "get": {
        "tags": [
          "Admin · Dashboard"
        ],
        "summary": "Dashboard summary counts",
        "description": "",
        "responses": {
          "200": {
            "description": "Summary: site mode, last change, post/user counts and recent activity",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "site_mode": {
                      "type": "string",
                      "example": "live"
                    },
                    "last_change": {
                      "type": "object",
                      "properties": {
                        "at": {
                          "type": "string",
                          "nullable": true
                        },
                        "by": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "counts": {
                      "type": "object",
                      "properties": {
                        "posts": {
                          "type": "object",
                          "additionalProperties": true
                        },
                        "users": {
                          "type": "integer"
                        }
                      }
                    },
                    "recent_activity": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/discord-bot/config": {
      "get": {
        "tags": [
          "Admin · Discord Bot"
        ],
        "summary": "Get Discord bot config + live status (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Masked config + live status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Discord Bot"
        ],
        "summary": "Save Discord bot config (admin only)",
        "description": "token is write-only — omit/blank it to keep the existing one unchanged.",
        "responses": {
          "200": {
            "description": "Updated config + live status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Validation error, invalid token, or missing token while enabling",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "guildId": {
                    "type": "string"
                  },
                  "token": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/email/config": {
      "get": {
        "tags": [
          "Admin · Email"
        ],
        "summary": "Get email delivery config + status (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Config (refresh token stripped) + status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Email"
        ],
        "summary": "Update email delivery config (admin only)",
        "description": "Set the From display name and enabled toggle. Enabling requires a connected Gmail account.",
        "responses": {
          "200": {
            "description": "Updated config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Cannot enable before connecting a mailbox",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "senderName": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/email/connect/callback": {
      "get": {
        "tags": [
          "Admin · Email"
        ],
        "summary": "OAuth2 callback — stores the refresh token, redirects to Settings",
        "description": "",
        "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": "Redirect back to /admin/settings"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/email/connect/start": {
      "get": {
        "tags": [
          "Admin · Email"
        ],
        "summary": "Begin the Gmail OAuth2 connect flow (admin only)",
        "description": "Returns { url } to redirect the browser to Google. Reuses the google SSO OAuth client.",
        "responses": {
          "200": {
            "description": "Authorization URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Google OAuth client not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/email/disconnect": {
      "post": {
        "tags": [
          "Admin · Email"
        ],
        "summary": "Disconnect Gmail and disable email (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Disconnected config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/email/test": {
      "post": {
        "tags": [
          "Admin · Email"
        ],
        "summary": "Send a test email (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sent": {
                      "type": "boolean"
                    },
                    "to": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "502": {
            "description": "Send failed / not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/invites": {
      "post": {
        "tags": [
          "Admin · Invites"
        ],
        "summary": "Create and email an account invite at a chosen access level",
        "description": "",
        "responses": {
          "201": {
            "description": "Invite created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {}
      },
      "get": {
        "tags": [
          "Admin · Invites"
        ],
        "summary": "List recent invites (no tokens)",
        "description": "",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invites, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/invites/{id}": {
      "delete": {
        "tags": [
          "Admin · Invites"
        ],
        "summary": "Revoke a pending invite",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Invite id."
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "No pending invite to revoke",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/appeals": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "List moderation appeals (default: pending + under_review)",
        "description": "Filter with ?status=<pending|under_review|approved|denied|withdrawn> or ?status=all. Paginated with ?limit&offset.",
        "responses": {
          "200": {
            "description": "Appeals queue",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AppealQueueItem"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/appeals/{id}": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Get a single moderation appeal",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Appeal id."
          }
        ],
        "responses": {
          "200": {
            "description": "The appeal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppealQueueItem"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Appeal not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/appeals/{id}/claim": {
      "post": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Claim a pending appeal (→ under_review)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Appeal id."
          }
        ],
        "responses": {
          "200": {
            "description": "The claimed appeal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppealQueueItem"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Appeal not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Appeal is not open for claiming",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/appeals/{id}/resolve": {
      "post": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Resolve an appeal (approved | denied); approval may auto-reverse the Discord action",
        "description": "Approving a ban/mute appeal best-effort asks the bot to reverse the Discord action (unban / clear timeout). The bot being down never fails the resolution — reversal_status is recorded as failed. The response echoes the updated appeal plus a `reversal` object.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Appeal id."
          }
        ],
        "responses": {
          "200": {
            "description": "The resolved appeal (with reversal outcome)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppealResolveResult"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (status must be approved or denied)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "404": {
            "description": "Appeal not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Appeal is already resolved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveAppealRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/moderation/filter-hits": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Recent automated content-filter hits",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/members": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Recent member join/leave events (optionally filtered by type)",
        "description": "",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/recent": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Recent moderation actions, optionally filtered by type",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/search": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Look up moderated users by Discord id or username snapshot",
        "description": "",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/spam-hits": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Recent automated spam-detection hits",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/stats/summary": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Moderation action counts for 24h/7d/30d (admin or moderator)",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/user/{discordId}": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Per-user moderation summary (counts, latest tag, linked account)",
        "description": "",
        "parameters": [
          {
            "name": "discordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/user/{discordId}/actions": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Full moderation action history for a user",
        "description": "",
        "parameters": [
          {
            "name": "discordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/user/{discordId}/appeals": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Appeals submitted for a Discord user",
        "description": "",
        "parameters": [
          {
            "name": "discordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Discord snowflake."
          }
        ],
        "responses": {
          "200": {
            "description": "Appeals for the user",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AppealQueueItem"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/moderation/user/{discordId}/notes": {
      "get": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Staff notes for a user (admin_only notes hidden from moderators)",
        "description": "",
        "parameters": [
          {
            "name": "discordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Moderation"
        ],
        "summary": "Add a staff note (admin_only visibility requires the admin role)",
        "description": "",
        "parameters": [
          {
            "name": "discordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "visibility": {
                    "example": "any"
                  },
                  "body": {
                    "example": "any"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/pages": {
      "get": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "List all CMS pages (summaries)",
        "description": "",
        "responses": {
          "200": {
            "description": "Page summaries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "Create a CMS page",
        "description": "",
        "responses": {
          "201": {
            "description": "Created page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid slug / title / blocks / metadata / settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slug already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "published"
                    ]
                  },
                  "blocks": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "metadata": {
                    "type": "object"
                  },
                  "settings": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/pages/{id}": {
      "get": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "Get a CMS page by id (full, incl. blocks)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Page id."
          }
        ],
        "responses": {
          "200": {
            "description": "The page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "patch": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "Update a CMS page (title, status, blocks, metadata, settings)",
        "description": "slug is immutable; disabling protection is rejected here (use /unprotect).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Page id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Validation error (slug immutable, invalid blocks, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Disabling protection requires /unprotect",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "Delete a CMS page (blocked if protected)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Page id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted (echoes the id)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedId"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Page is protected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/pages/{id}/preview": {
      "post": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "Mint a 1h draft-preview link for a page",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Page id."
          }
        ],
        "responses": {
          "200": {
            "description": "Preview token + path",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "expiresInSeconds": {
                      "type": "integer"
                    },
                    "path": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/pages/{id}/unprotect": {
      "post": {
        "tags": [
          "Admin · Pages"
        ],
        "summary": "Disable page protection (password step-up re-auth)",
        "description": "Verifies the current admin password server-side, then flips protected → false.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Page id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated page (protected=false)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Password incorrect",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "password": {
                    "type": "string"
                  }
                },
                "required": [
                  "password"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/posts": {
      "get": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "List all posts (including unpublished)",
        "description": "",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional category filter."
          }
        ],
        "responses": {
          "200": {
            "description": "Posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Post"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Create a post",
        "description": "",
        "responses": {
          "201": {
            "description": "Created post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unknown category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostCreateRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/posts/upload": {
      "post": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Upload a post image (multipart)",
        "description": "",
        "responses": {
          "201": {
            "description": "Stored image URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "image_url": {
                      "type": "string",
                      "example": "/uploads/1700000000-abcd.png"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No image / disallowed type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/posts/{id}": {
      "get": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Get a post by id",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Post id."
          }
        ],
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Update a post",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Post id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unknown category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostCreateRequest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Delete a post",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Post id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted (echoes the id)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedId"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/posts/{id}/announce": {
      "get": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Get the announcement pipeline status for a post",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Post id."
          }
        ],
        "responses": {
          "200": {
            "description": "The announce job for the post, or null if never announced",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "nullable": true,
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/posts/{id}/announce/retry": {
      "post": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Retry one announcement delivery leg (town crier or Discord)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Post id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated announce job",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "No announcement job for this post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leg": {
                    "type": "string",
                    "enum": [
                      "towncrier",
                      "discord"
                    ]
                  }
                },
                "required": [
                  "leg"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/posts/{id}/publish": {
      "patch": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Publish / unpublish a post",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Post id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublishRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/settings": {
      "get": {
        "tags": [
          "Admin · Settings"
        ],
        "summary": "Get all site settings (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "All settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Settings"
        ],
        "summary": "Update site settings (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Updated settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Body must be an object of key/value settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "description": "An object of key/value settings."
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/settings/{key}": {
      "delete": {
        "tags": [
          "Admin · Settings"
        ],
        "summary": "Reset one setting to its default (admin only)",
        "description": "Deletes the settings row so the surface falls back to its BRAND_* env / theme.css / hardcoded default. Restricted to the resettable keys (theme_visual, brand_assets, nav_public, nav_admin, nav_player, hero_layout_draft). Idempotent: resetting a key that was never set succeeds.",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Settings key to reset"
          }
        ],
        "responses": {
          "200": {
            "description": "Setting reset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Setting is not resettable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/account": {
      "post": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Create a game account and link it to the caller (staff self-service)",
        "description": "Same as POST /player/shard/account but for a signed-in staff user — provisions a game account (own username + password) and links it. Gated by game_account_signup + the shard’s mode; the password is never stored or logged.",
        "responses": {
          "201": {
            "description": "Account created and linked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Game-account signup unavailable (site or shard)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Account name already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {}
      }
    },
    "/api/v1/admin/shard/accounts": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "List the caller’s linked game accounts (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Linked accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardLink"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/atlas": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Spawn atlas status: path, drift, counts, pending review (admin only)",
        "description": "Where the ServUO tree is, whether it can be read, whether its source files have drifted from the loaded atlas, and any refresh staged for approval. The public /atlas/meta route reports the game world only; the filesystem detail is here.",
        "responses": {
          "200": {
            "description": "Atlas status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasStatus"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/atlas/approve": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Approve a staged atlas refresh that removes a facet (admin only)",
        "description": "Re-parses the tree and applies it, facet loss included. Only the decision was stored, never the parsed world, so what lands matches the tree at approval time — an operator who has since fixed a half-copied mount gets the corrected import.",
        "responses": {
          "200": {
            "description": "What happened",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasRefreshResult"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/atlas/import": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Re-import the spawn atlas from the ServUO tree (admin only)",
        "description": "Applies a map change without a restart. `force` reimports even when the source hashes match what is loaded. A refresh that would REMOVE a facet is still staged for approval rather than applied — that decision is never taken implicitly. An unreadable tree answers 200 with status \"unavailable\" rather than 500: the refresh contract reports outcomes instead of throwing, and the admin needs to be told what is wrong with the path.",
        "responses": {
          "200": {
            "description": "What happened",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasRefreshResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "force": {
                    "type": "boolean",
                    "description": "Reimport even if the tree is unchanged."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/atlas/path": {
      "put": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Set the ServUO tree the atlas reads from (admin only)",
        "description": "Persisted as a setting, which wins over the SERVUO_PATH deploy default so the mount can move without a redeploy. Blank clears it and the atlas is simply skipped on the next boot. Deliberately does not import as a side effect — the response carries the refreshed status so the panel can offer that as the next step.",
        "responses": {
          "200": {
            "description": "Atlas status after the change",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasStatus"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "path"
                ],
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Absolute path to the ServUO server root. Blank disables the atlas."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/atlas/reject": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Reject a staged atlas refresh (admin only)",
        "description": "Keeps the current atlas and remembers the decision against those exact source hashes, so a declined refresh does not re-prompt on every restart. Changing the tree asks again.",
        "responses": {
          "200": {
            "description": "Rejected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasRefreshResult"
                }
              }
            }
          },
          "404": {
            "description": "Nothing is awaiting review",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/audit": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Recent in-game moderation audit events (admin/moderator)",
        "description": "",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "admin.audit events, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardEvent"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/ban": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Ban an account, timed or indefinite (admin/moderator)",
        "description": "",
        "responses": {
          "200": {
            "description": "Banned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Protected target or write plane disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "serial": {
                    "type": "string"
                  },
                  "durationSec": {
                    "type": "integer"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/broadcast": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Broadcast a system message to everyone online (admin/moderator)",
        "description": "",
        "responses": {
          "200": {
            "description": "Broadcast",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string"
                  },
                  "hue": {
                    "type": "integer"
                  }
                },
                "required": [
                  "text"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/char/{serial}": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Character sheet (self-linked characters; admins: any character)",
        "description": "",
        "parameters": [
          {
            "name": "serial",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mobile serial, e.g. 0x24C."
          }
        ],
        "responses": {
          "200": {
            "description": "Character profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Character not on an account linked to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Service Unavailable"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/clilocs": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Cliloc table status: sources, drift, entry count (admin only)",
        "description": "Where the cliloc sources are, whether they can be read, how many entries are loaded, and whether the files on disk have drifted from them. The table is built from a SET of sources — the converted client table plus every operator-maintained overlay under `custom/`, which is how shard-added and shard-edited items get names. `missingSources` lists any source that was loaded before and is now gone; an import refuses that without `approve`. A shard with nothing configured is a supported state — item names simply render as ids.",
        "responses": {
          "200": {
            "description": "Cliloc status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClilocStatus"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/clilocs/import": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Re-import the cliloc table from its source files (admin only)",
        "description": "Applies a client patch, or a change to the shard\\'s own overlay files, without a restart. `force` reimports even when the source hashes match what is loaded. `approve` accepts a refresh in which a previously-loaded source has VANISHED — refused by default, because an unmounted volume and a deliberate deletion are indistinguishable from the server, and the wrong guess silently drops every name that file contributed. A missing path — or the common mistake of pointing at the client\\'s own COMPRESSED Cliloc.enu — answers 200 with status \"unavailable\" and the reason, rather than 500: the refresh contract reports outcomes instead of throwing, and the admin needs to be told which file to convert.",
        "responses": {
          "200": {
            "description": "What happened",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClilocRefreshResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "force": {
                    "type": "boolean",
                    "description": "Reimport even if the sources are unchanged."
                  },
                  "approve": {
                    "type": "boolean",
                    "description": "Accept a refresh in which a previously-loaded source has vanished."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/clilocs/path": {
      "put": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Set the cliloc source the site reads from (admin only)",
        "description": "Accepts either the converted base file itself or a directory to search. Overlays are read from a `custom/` directory beside it either way — pointing at a file does not forfeit them. Persisted as a setting, which wins over the UO_CLIENT_PATH deploy default so the mount can move without a redeploy. Blank clears it and resolution is skipped on the next boot. Deliberately does not import as a side effect — the response carries the refreshed status so the panel can offer that as the next step.",
        "responses": {
          "200": {
            "description": "Cliloc status after the change",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClilocStatus"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "path"
                ],
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Path to the converted cliloc file, or a directory containing one. Blank disables resolution."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/houses": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Full house registry — owner, price, decay (admin/moderator)",
        "description": "The complete house registry. The public endpoint shows only IDOC houses with location; this staff view carries owner/price/co-owner/decay detail.",
        "responses": {
          "200": {
            "description": "Houses, ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardHouse"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/kick": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Kick every live session of an account (admin/moderator)",
        "description": "",
        "responses": {
          "200": {
            "description": "Kicked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Protected target or write plane disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "serial": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/link": {
      "post": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Link an in-game account with a one-time code (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Linked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardLinkResult"
                }
              }
            }
          },
          "400": {
            "description": "Unknown or expired code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Service Unavailable"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShardLinkRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/pages": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Open help-page (support) queue (admin/moderator)",
        "description": "",
        "responses": {
          "200": {
            "description": "Open pages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/pages/{id}/close": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Resolve a help page without a reply (admin/moderator)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Page id (sender serial)."
          }
        ],
        "responses": {
          "200": {
            "description": "Closed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/pages/{id}/respond": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Reply to a help page, optionally closing it (admin/moderator)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Page id (sender serial)."
          }
        ],
        "responses": {
          "200": {
            "description": "Responded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Unknown page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string"
                  },
                  "close": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/roster/{account}": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Character roster for an account (self; admins: any account)",
        "description": "",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "A game account linked to the caller."
          }
        ],
        "responses": {
          "200": {
            "description": "Account roster",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Account not linked to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/sales": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Recent player-vendor sales for the caller’s linked accounts (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Vendor sales",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardVendorSale"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/unban": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Clear an account ban (admin/moderator)",
        "description": "",
        "responses": {
          "200": {
            "description": "Unbanned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  }
                },
                "required": [
                  "account"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/shard/vendors/{account}": {
      "get": {
        "tags": [
          "Admin · Account"
        ],
        "summary": "Player vendors for an account (self; admins: any account)",
        "description": "",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "A game account linked to the caller."
          }
        ],
        "responses": {
          "200": {
            "description": "Vendor snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Account not linked to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/shard/visibility": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Get per-feature shard visibility config (admin only)",
        "description": "The effective config (compiled defaults merged with stored overrides) plus the vocabulary the admin UI renders from: the audience ladder and the always-locked fields. Defaults reproduce pre-v3 behavior.",
        "responses": {
          "200": {
            "description": "Visibility config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardVisibilityConfig"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Update per-feature shard visibility config (admin only)",
        "description": "Patch one or more features. Unknown feature names, unknown rungs, and any attempt to configure a locked field (acct / webId — admin-only always) are rejected with 400 rather than silently dropped.",
        "responses": {
          "200": {
            "description": "Updated config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardVisibilityConfig"
                }
              }
            }
          },
          "400": {
            "description": "Unknown feature, rung, or a locked field",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShardVisibilityUpdate"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/site-mode": {
      "put": {
        "tags": [
          "Admin · Dashboard"
        ],
        "summary": "Set site mode (admin only)",
        "description": "Switch the site between live and maintenance.",
        "responses": {
          "200": {
            "description": "Updated site mode",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteModeState"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SiteModeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/uo-link/config": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Get uo-link config + live status + ingestion stats (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Masked config, health and ingestion stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Save uo-link connection config (admin only)",
        "description": "token is write-only — omit/blank it to keep the existing one. Saving (re)starts the WS ingest client.",
        "responses": {
          "200": {
            "description": "Updated config + live status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or missing token while enabling",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "baseUrl": {
                    "type": "string"
                  },
                  "wsUrl": {
                    "type": "string"
                  },
                  "token": {
                    "type": "string"
                  },
                  "protocol": {
                    "type": "integer"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/uo-link/stream": {
      "get": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Full live shard event stream incl. audit/cheat (SSE, admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "An SSE stream (Content-Type: text/event-stream)."
          }
        }
      }
    },
    "/api/v1/admin/uo-link/towncrier": {
      "post": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Publish / replace a town-crier message (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Posted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Rejected (over caps)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Shard unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TownCrierRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/uo-link/towncrier/{id}": {
      "delete": {
        "tags": [
          "Admin · Shard"
        ],
        "summary": "Remove a town-crier message (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Town-crier message id."
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Service Unavailable"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/uploads": {
      "post": {
        "tags": [
          "Admin · Posts"
        ],
        "summary": "Upload an image for rich-text editors (multipart)",
        "description": "",
        "responses": {
          "201": {
            "description": "Stored file URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadResponse"
                }
              }
            }
          },
          "400": {
            "description": "No file / disallowed type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/users": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "List users (admin only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/User"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Create a user (admin only)",
        "description": "",
        "responses": {
          "201": {
            "description": "Created user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Username already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/users/{id}": {
      "put": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Update a user (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or cannot demote the last admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Username already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateRequest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Delete a user (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted (echoes the id)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedId"
                }
              }
            }
          },
          "400": {
            "description": "Cannot delete your own account or the last admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Get a single user (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "The user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/mfa/reset": {
      "post": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Reset a user’s MFA (admin only)",
        "description": "Recovers a locked-out user: turns TOTP off, revokes every trusted device, and clears their recovery codes. The user can then sign in with their password alone and re-enroll.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "MFA reset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkFlag"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/shard/accounts": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "A user’s linked game accounts (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Linked accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardLink"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/shard/houses": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Houses owned by a user’s accounts (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Houses (IDOC first)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/shard/link/{account}": {
      "delete": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Unlink a game account from this user (admin only)",
        "description": "Severs a game account’s tie to the website user from the site side (sidecar DELETE /link/{account}) and drops the local mirror. actor is stamped from the session.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          },
          {
            "name": "account",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Game account to unlink."
          }
        ],
        "responses": {
          "200": {
            "description": "Unlinked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "string"
                    },
                    "unlinked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Protected staff account (refused by shard)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not linked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Service Unavailable"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/shard/online": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "A user’s characters currently online (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Online characters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/shard/sales": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Recent vendor sales on a user’s accounts (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Vendor sales",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardVendorSale"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/shard/standing": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "A user’s shard standing — governorships held and guilds led (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Standing { governorOf, guildsLed }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/trusted-devices": {
      "get": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "List a user’s trusted devices (admin only)",
        "description": "Active (unrevoked, unexpired) trusted devices for the target user — the browsers/apps allowed to skip that user’s TOTP step. Never returns tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Trusted devices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrustedDevice"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Revoke all of a user’s trusted devices (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/users/{id}/trusted-devices/{deviceId}": {
      "delete": {
        "tags": [
          "Admin · Users"
        ],
        "summary": "Revoke one of a user’s trusted devices (admin only)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "User id."
          },
          {
            "name": "deviceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Trusted-device id."
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked (idempotent)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/wiki": {
      "get": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "List all wiki pages (including unpublished)",
        "description": "",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Wiki pages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WikiPage"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Create a wiki page",
        "description": "",
        "responses": {
          "201": {
            "description": "Created wiki page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiPage"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unknown category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slug already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WikiPageCreateRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/wiki/categories": {
      "get": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "List wiki categories",
        "description": "",
        "responses": {
          "200": {
            "description": "Wiki categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WikiCategory"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Create a wiki category",
        "description": "",
        "responses": {
          "201": {
            "description": "Created category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiCategory"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slug already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WikiCategoryCreateRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/wiki/categories/{id}": {
      "put": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Update a wiki category",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Category id."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiCategory"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slug already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WikiCategoryCreateRequest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Delete a wiki category",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Category id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted (echoes the id)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedId"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/wiki/tags": {
      "get": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "List wiki tags",
        "description": "",
        "responses": {
          "200": {
            "description": "Wiki tags",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/wiki/{slug}": {
      "get": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Get a wiki page by slug",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "The wiki page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiPage"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Update a wiki page (creates a revision)",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated wiki page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiPage"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unknown category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WikiPageCreateRequest"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "change_note": {
                        "type": "string",
                        "maxLength": 280
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Delete a wiki page",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted (echoes the slug)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedSlug"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/wiki/{slug}/publish": {
      "patch": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Publish / unpublish a wiki page",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "Updated wiki page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiPage"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublishRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/wiki/{slug}/revisions": {
      "get": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "List revisions of a wiki page",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "Revisions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/wiki/{slug}/revisions/{id}": {
      "get": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Get a single wiki revision",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Revision id."
          }
        ],
        "responses": {
          "200": {
            "description": "The revision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/admin/wiki/{slug}/revisions/{id}/restore": {
      "post": {
        "tags": [
          "Admin · Wiki"
        ],
        "summary": "Restore a wiki page to a revision",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Revision id to restore."
          }
        ],
        "responses": {
          "200": {
            "description": "Restored wiki page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiPage"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/invite/{token}": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Look up an email invite by token",
        "description": "Returns the pre-assigned email + role for a valid, pending, unexpired invite so the accept form can render. 404 for anything not currently acceptable.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invite details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Invalid or expired invite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/auth/invite/{token}/accept": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Accept an email invite (creates the account at the invited role)",
        "description": "Creates the website user at the invite’s pre-assigned role and logs them in (sets the session cookie). Bypasses the player_registration gate — the invite is its own authority. Rate limited + honeypot-guarded like registration.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Account created and session issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "404": {
            "description": "Invalid or expired invite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Username taken or invite already used",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {}
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Log in with username and password",
        "description": "On success sets the httpOnly session cookie. If the account has 2FA enabled, returns { totpRequired, challenge } instead and no cookie is set — complete login at POST /login/totp. Rate limited and behind bot/backoff guards.",
        "responses": {
          "200": {
            "description": "Session issued, or TOTP challenge required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Incorrect username or password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "429": {
            "description": "Too many attempts (rate limited / backoff)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/login/totp": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Complete login with a TOTP or recovery code",
        "description": "Second step for 2FA accounts. Exchange the challenge from /login plus either the current authenticator code OR a single-use recovery code for a session cookie. Set trustDevice to remember this browser and skip TOTP on future logins (30 days); if the trusted-device limit is reached the session is still issued and the response carries { trustLimitReached, devices } so the user can revoke one first.",
        "responses": {
          "200": {
            "description": "Session issued (optionally with a trusted-device-limit prompt)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid code or expired challenge",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts (rate limited / backoff)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpLoginRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Log out (clear the cookie and revoke this session)",
        "description": "",
        "responses": {
          "200": {
            "description": "Logged out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Current authenticated user",
        "description": "",
        "responses": {
          "200": {
            "description": "The signed-in user",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/account": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Get the current account (self, any role)",
        "description": "",
        "responses": {
          "200": {
            "description": "The current account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerAccount"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Account not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/account/identities": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "List linked SSO identities (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Linked identities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LinkedIdentity"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/account/identities/{provider}": {
      "delete": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Unlink an SSO identity (self)",
        "description": "",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id."
          }
        ],
        "responses": {
          "200": {
            "description": "Unlinked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnlinkedFlag"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "No linked account for that provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/account/password": {
      "patch": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Change or set the current account’s password (self, any role)",
        "description": "If the account already has a password, currentPassword is required and verified. SSO-provisioned accounts with no password may set an initial one without a current password. On success the caller’s own session is re-issued (they stay logged in) while older web sessions are revoked.",
        "responses": {
          "200": {
            "description": "Password changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkFlag"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or wrong current password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "429": {
            "description": "Too many changes (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangePasswordRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me/account/recovery-codes/generate": {
      "post": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Regenerate recovery codes (self, password step-up)",
        "description": "Generates a fresh set of single-use recovery codes, invalidating any prior set, and returns them ONCE. Requires the current password (accounts that have one); refuses when two-factor is off. Behind the login backoff/bot guards since a wrong password is credential-guessing.",
        "responses": {
          "200": {
            "description": "New recovery codes (shown once)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecoveryCodes"
                }
              }
            }
          },
          "400": {
            "description": "Wrong password, or two-factor not enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "429": {
            "description": "Too Many Requests"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "currentPassword": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me/account/recovery-codes/status": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Remaining recovery-code count (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Remaining unused codes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "remaining": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/account/totp/disable": {
      "post": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Disable 2FA by confirming a code",
        "description": "Requires a valid current authenticator code (proves control of the authenticator); it does not take a password.",
        "responses": {
          "200": {
            "description": "2FA disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpState"
                }
              }
            }
          },
          "400": {
            "description": "Not enabled, or invalid code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpCodeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me/account/totp/enable": {
      "post": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Enable 2FA by confirming a code",
        "description": "",
        "responses": {
          "200": {
            "description": "2FA enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpState"
                }
              }
            }
          },
          "400": {
            "description": "Setup not started, or invalid code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Two-factor already enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpCodeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me/account/totp/setup": {
      "post": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Begin 2FA enrollment (returns secret + QR)",
        "description": "",
        "responses": {
          "200": {
            "description": "otpauth URL and QR data to scan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpSetup"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Two-factor already enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/account/username": {
      "patch": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Change the current account’s username (self, any role)",
        "description": "",
        "responses": {
          "200": {
            "description": "Updated username (session re-issued)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "username": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unavailable username",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Username already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many changes (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeUsernameRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me/devices": {
      "post": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Register a push device (endpoint) for the current user",
        "description": "Registers a UnifiedPush/ntfy endpoint (or an FCM token) so the backend can deliver opt-in push tickles. The endpoint must be an allowed HTTPS relay URL — private/loopback hosts and non-allowed origins are rejected 400. Idempotent per (user, endpoint).",
        "responses": {
          "201": {
            "description": "Device registered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PushDevice"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or disallowed endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterDeviceRequest"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "List the current user’s registered push devices",
        "description": "",
        "responses": {
          "200": {
            "description": "Registered devices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PushDevice"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/devices/{id}": {
      "delete": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Unregister a push device",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Device id (must belong to the caller)."
          }
        ],
        "responses": {
          "200": {
            "description": "Unregistered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkFlag"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "No such device for this user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/notifications/streams": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "List subscribable notification streams (catalog)",
        "description": "The catalog of push streams. `personal`/`requiresLinkedAccount` streams are delivered only to the owning user and need a linked game account.",
        "responses": {
          "200": {
            "description": "Stream catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationStreams"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/notifications/subscriptions": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Get the current user’s notification subscriptions",
        "description": "",
        "responses": {
          "200": {
            "description": "Subscribed stream ids",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationSubscriptions"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Replace the current user’s notification subscriptions",
        "description": "Sets the full opted-in stream set (applied to all the user’s devices). Unknown stream ids are ignored; the stored set is echoed back.",
        "responses": {
          "200": {
            "description": "Updated subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationSubscriptions"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationSubscriptions"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me/sessions": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "List active mobile device sessions (self)",
        "description": "Active (unrevoked, unexpired) mobile bearer sessions — one per live device — for the Active Devices screen. Never returns tokens.",
        "responses": {
          "200": {
            "description": "Active device sessions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DeviceSession"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/sessions/{id}": {
      "delete": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Revoke one mobile device session (self)",
        "description": "Revokes a single device by its session id (ownership-scoped). Revoking stops future token renewals; an already-issued access token remains valid until it expires (see the documented revocation-latency window).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The session row id from GET /auth/me/sessions."
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked (idempotent)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/trusted-devices": {
      "get": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "List trusted devices (self)",
        "description": "Active (unrevoked, unexpired) trusted devices — the browsers/apps allowed to skip the TOTP step at login. Never returns tokens.",
        "responses": {
          "200": {
            "description": "Active trusted devices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrustedDevice"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Trust the current device (self)",
        "description": "Marks the current browser/app as trusted so future logins skip the TOTP step (30 days). Web receives an httpOnly trust cookie; native to store. Returns 409 { error: \"trusted_device_limit\", devices } when the per-user cap is reached — revoke one first, then retry.",
        "responses": {
          "200": {
            "description": "Device trusted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrustDeviceResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Trusted-device limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrustedDeviceLimit"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "deviceName": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Revoke all trusted devices (self)",
        "description": "Untrust every device; future logins on all of them require the full TOTP step again. Also clears this browser’s trust cookie.",
        "responses": {
          "200": {
            "description": "Revoked count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/me/trusted-devices/{id}": {
      "delete": {
        "tags": [
          "Auth · Me"
        ],
        "summary": "Revoke one trusted device (self)",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Trusted-device id from GET /auth/me/trusted-devices."
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked (idempotent)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/mobile/login": {
      "post": {
        "tags": [
          "Auth · Mobile"
        ],
        "summary": "Native login → access + refresh tokens",
        "description": "Bearer-token login for native clients. Single-request 2FA: if the account has TOTP on and no/invalid code is supplied, returns 401 { totpRequired: true } and the client retries with a code (or a single-use recoveryCode). A previously trusted device may present the X-Trust-Token header to skip the code entirely. Set trustDevice to remember this device (the response then carries trustToken to store); if the trusted-device limit is reached the tokens are still issued and the response carries { trustLimitReached, devices }.",
        "responses": {
          "200": {
            "description": "Access + refresh tokens (optionally with trustToken / a trusted-device-limit prompt)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MobileTokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials, or a TOTP code is required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts (rate limited / backoff)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MobileLoginRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/mobile/logout": {
      "post": {
        "tags": [
          "Auth · Mobile"
        ],
        "summary": "Revoke the current (or all) refresh tokens",
        "description": "Requires a valid bearer access token. Revokes the given refresh token, or every session for the user when { all: true }. Idempotent.",
        "responses": {
          "200": {
            "description": "Logged out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Missing or invalid bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MobileLogoutRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/mobile/refresh": {
      "post": {
        "tags": [
          "Auth · Mobile"
        ],
        "summary": "Rotate a refresh token for a fresh token pair",
        "description": "Refresh tokens are single-use: the presented token is revoked and a new access + refresh pair is issued. Reusing a rotated token fails with 401.",
        "responses": {
          "200": {
            "description": "New access + refresh tokens",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MobileTokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or expired session",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many refresh attempts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MobileRefreshRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/mobile/sso/exchange": {
      "post": {
        "tags": [
          "Auth · Mobile"
        ],
        "summary": "Exchange an SSO authorization code for mobile tokens",
        "description": "Redeems the single-use authorization code returned to the app callback, together with the PKCE code_verifier, for the SAME access + refresh pair as /auth/mobile/login. The code is single-use and PKCE-bound: a wrong verifier, an expired/used code, or a reused code all fail 401. If the user ticked \"trust this device\" on the TOTP form during this flow, the response also carries { trustToken } for the app to store and replay via X-Trust-Token — minted here rather than passed through the deep link so it never appears in a URL.",
        "responses": {
          "200": {
            "description": "Access + refresh tokens (optionally with a trustToken to persist)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MobileTokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid/expired/used code or failed PKCE verification",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MobileSsoExchangeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/mobile/sso/start": {
      "get": {
        "tags": [
          "Auth · Mobile"
        ],
        "summary": "Begin native SSO login (redirect to the IdP)",
        "description": "Opened by the Android app in a Custom Tab. Validates the provider is enabled and the redirect_uri is an exact match of a registered app callback, seeds a short-lived bridge session carrying the app PKCE challenge + state, and 302-redirects into the existing website SSO flow. On success the callback redirects to `redirect_uri?code=…&state=…` (a one-time code, never a token). Errors are surfaced to the app as `redirect_uri?error=…&state=…`.",
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id from GET /auth/providers (e.g. google, discord)."
          },
          {
            "name": "code_challenge",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "App-generated PKCE S256 challenge (base64url)."
          },
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "App-generated opaque CSRF value, echoed on the callback for the app to verify."
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The app callback; must EXACTLY match a registered value (default runicgateway://auth/callback)."
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the identity provider (or back to the app callback on error)"
          },
          "400": {
            "description": "Unrecognized redirect URI or validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/password/forgot": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Request a password-reset link by email",
        "description": "Emails a single-use, ~1h reset link to every active account on the address. Always returns the same generic 200 whether or not the email matches (no account enumeration). Email is non-unique, so multiple accounts may each receive a link naming their username. Rate limited per IP.",
        "responses": {
          "200": {
            "description": "Generic acknowledgement (sent if the account exists)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/password/reset/{token}": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Validate a password-reset link",
        "description": "Returns the target username for a valid, pending, unexpired reset link so the reset form can render. 404 for anything not currently usable (never distinguishes expired from used from never-existed).",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reset link is valid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "username": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "404": {
            "description": "Invalid or expired reset link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      },
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Set a new password from a reset link",
        "description": "Consumes the single-use link and sets the new password. Rotates the hash and revokes every existing session (web + mobile). Does NOT sign the user in — they log in fresh afterwards (so a 2FA account still passes TOTP). Rate limited per IP.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Password changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "404": {
            "description": "Invalid, expired, or already-used reset link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "password"
                ],
                "properties": {
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/providers": {
      "get": {
        "tags": [
          "Auth · SSO"
        ],
        "summary": "List enabled SSO providers",
        "description": "Public discovery used by the login page to render provider buttons. Never exposes secrets.",
        "responses": {
          "200": {
            "description": "Enabled, valid providers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Provider"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/auth/register": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Register a player account",
        "description": "Creates a self-service player account and logs it in (sets the session cookie). Available only when an admin has enabled password registration (player_registration = password|both); otherwise returns 403. Rate limited and behind bot/backoff guards; a hidden honeypot field must stay empty.",
        "responses": {
          "200": {
            "description": "Account created and session issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unavailable username",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "403": {
            "description": "Registration is not open",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Username already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts (rate limited / backoff)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/sso/totp": {
      "post": {
        "tags": [
          "Auth · SSO"
        ],
        "summary": "Complete an SSO login with a TOTP code",
        "description": "Second step when a linked account has 2FA enabled. Reads the staged pending-TOTP cookie set by the callback plus the current authenticator code, and on success sets the session cookie. Set trustDevice to remember this browser and skip TOTP on future SSO sign-ins (30 days) — on the mobile flow this browser is the app Custom Tab, and the app additionally receives its own trustToken at /auth/mobile/sso/exchange. If the trusted-device limit is reached the sign-in still completes and the response carries { trustLimitReached, devices }. Rate limited and behind bot/backoff guards.",
        "responses": {
          "200": {
            "description": "Session issued (web), or a deep link to redeem (mobile bridge); optionally with a trusted-device-limit prompt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/SafeUser"
                    },
                    "returnTo": {
                      "type": "string"
                    },
                    "redirect": {
                      "type": "string"
                    },
                    "trustLimitReached": {
                      "type": "boolean"
                    },
                    "devices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TrustedDevice"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Invalid code or expired challenge",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Conflict"
          },
          "429": {
            "description": "Too many attempts (rate limited / backoff)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "trustDevice": {
                    "type": "boolean"
                  },
                  "deviceName": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/sso/{provider}/callback": {
      "get": {
        "tags": [
          "Auth · SSO"
        ],
        "summary": "OAuth redirect target — completes login or linking",
        "description": "The provider redirects here with code + state. On success sets the session cookie (login) or links the identity (link), then 302-redirects into /admin. Login is link-only: unknown identities are refused (sso_error=not_linked).",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id (e.g. google, discord)."
          },
          {
            "name": "error",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "OAuth authorization code."
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "OAuth state (matched against the tx cookie)."
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect into /admin on success, or back to login/account with an error code"
          }
        }
      }
    },
    "/api/v1/auth/sso/{provider}/link": {
      "get": {
        "tags": [
          "Auth · SSO"
        ],
        "summary": "Begin linking an SSO identity to the current account",
        "description": "Requires an authenticated session; the signed transaction captures the acting user so the callback can attach the external identity.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id (e.g. google, discord)."
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the identity provider (or back to the account page on error)"
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/auth/sso/{provider}/start": {
      "get": {
        "tags": [
          "Auth · SSO"
        ],
        "summary": "Begin SSO login (redirect to the IdP)",
        "description": "Sets a short-lived signed transaction cookie and 302-redirects to the provider authorize URL. On error redirects back to the login page with an sso_error query param.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id (e.g. google, discord)."
          },
          {
            "name": "returnTo",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Internal /admin path to return to after login."
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the identity provider (or back to the login page on error)"
          }
        }
      }
    },
    "/api/v1/player/account": {
      "get": {
        "tags": [
          "Player"
        ],
        "summary": "Get the current player account (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "The player account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerAccount"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Account not active (disabled/banned)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/account/identities": {
      "get": {
        "tags": [
          "Player"
        ],
        "summary": "List linked SSO identities (self)",
        "description": "",
        "responses": {
          "200": {
            "description": "Linked identities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LinkedIdentity"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/account/identities/{provider}": {
      "delete": {
        "tags": [
          "Player"
        ],
        "summary": "Unlink an SSO identity (self)",
        "description": "",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Provider id."
          }
        ],
        "responses": {
          "200": {
            "description": "Unlinked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnlinkedFlag"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "No linked account for that provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/account/password": {
      "patch": {
        "tags": [
          "Player"
        ],
        "summary": "Change or set the current player’s password",
        "description": "If the account already has a password, currentPassword is required and verified. SSO-provisioned accounts with no password may set an initial one without a current password. On success the caller’s session is re-issued (they stay logged in) while all other sessions are revoked.",
        "responses": {
          "200": {
            "description": "Password changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkFlag"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or wrong current password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Account not active (disabled/banned)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many changes (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangePasswordRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/player/account/totp/disable": {
      "post": {
        "tags": [
          "Player"
        ],
        "summary": "Disable 2FA by confirming a code",
        "description": "Requires a valid current authenticator code (proves control of the authenticator); it does not take a password.",
        "responses": {
          "200": {
            "description": "2FA disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpState"
                }
              }
            }
          },
          "400": {
            "description": "Not enabled, or invalid code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpCodeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/player/account/totp/enable": {
      "post": {
        "tags": [
          "Player"
        ],
        "summary": "Enable 2FA by confirming a code",
        "description": "",
        "responses": {
          "200": {
            "description": "2FA enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpState"
                }
              }
            }
          },
          "400": {
            "description": "Setup not started, or invalid code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Two-factor already enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpCodeRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/player/account/totp/setup": {
      "post": {
        "tags": [
          "Player"
        ],
        "summary": "Begin 2FA enrollment (returns secret + QR)",
        "description": "",
        "responses": {
          "200": {
            "description": "otpauth URL and QR data to scan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpSetup"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Two-factor already enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/account/username": {
      "patch": {
        "tags": [
          "Player"
        ],
        "summary": "Change the current player’s username",
        "description": "",
        "responses": {
          "200": {
            "description": "Updated username (session cookie re-issued)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "username": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or unavailable username",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Account not active (disabled/banned)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Username already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many changes (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeUsernameRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/player/appeals": {
      "get": {
        "tags": [
          "Player · Appeals"
        ],
        "summary": "List the caller’s moderation appeals",
        "description": "",
        "responses": {
          "200": {
            "description": "The caller’s appeals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Appeal"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Account not active (disabled/banned)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "Player · Appeals"
        ],
        "summary": "Submit a moderation appeal for one of the caller’s actions",
        "description": "Opens an appeal for a ban/mute mod_action that belongs to the caller (its target matches the caller’s linked Discord identity) and has no active appeal.",
        "responses": {
          "201": {
            "description": "Appeal created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appeal"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or the action type is not appealable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "The action does not belong to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Mod action not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "An appeal for this action is already open",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAppealRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/player/appeals/eligible": {
      "get": {
        "tags": [
          "Player · Appeals"
        ],
        "summary": "List the caller’s ban/mute actions eligible for appeal",
        "description": "The caller’s ban/mute mod_actions that have no active appeal. Returns an empty array when the caller has no linked Discord account (the UI shows a “link Discord” hint).",
        "responses": {
          "200": {
            "description": "Appealable actions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AppealEligibleAction"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Account not active (disabled/banned)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/appeals/{id}/withdraw": {
      "post": {
        "tags": [
          "Player · Appeals"
        ],
        "summary": "Withdraw one of the caller’s pending appeals",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Appeal id (must belong to the caller)."
          }
        ],
        "responses": {
          "200": {
            "description": "The withdrawn appeal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appeal"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "No such appeal for the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Appeal is already resolved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/shard/account": {
      "post": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "Create a game account (hybrid signup) and link it to the caller",
        "description": "Provisions a new game account with its own username + password and auto-links it to the signed-in website user. Available only when game_account_signup is enabled and the shard accepts website signups. The password is hashed on the shard and never stored or logged by the site.",
        "responses": {
          "201": {
            "description": "Account created and linked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "string"
                    },
                    "linked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or rejected name/password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Game-account signup unavailable (site or shard)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Account name already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-IP account cap reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Shard unavailable — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {}
      }
    },
    "/api/v1/player/shard/accounts": {
      "get": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "List the caller’s linked game accounts",
        "description": "",
        "responses": {
          "200": {
            "description": "Linked accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardLink"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/shard/char/{serial}": {
      "get": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "Character sheet — only for a character on the caller’s linked account",
        "description": "",
        "parameters": [
          {
            "name": "serial",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mobile serial, e.g. 0x24C."
          }
        ],
        "responses": {
          "200": {
            "description": "Character profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Character not on an account linked to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Shard unavailable — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/shard/houses": {
      "get": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "The caller’s own houses (home status)",
        "description": "Houses owned by the caller’s linked accounts, with decay/IDOC status. Only the caller’s own houses — never anyone else’s.",
        "responses": {
          "200": {
            "description": "The caller’s houses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardHouse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/shard/link": {
      "post": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "Link an in-game account with a one-time code",
        "description": "The player runs [link in game to get a code, then submits it here. The server confirms it with the sidecar and mirrors the link.",
        "responses": {
          "200": {
            "description": "Linked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardLinkResult"
                }
              }
            }
          },
          "400": {
            "description": "Unknown or expired code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "502": {
            "description": "Bad Gateway"
          },
          "503": {
            "description": "Shard unavailable — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShardLinkRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/player/shard/roster/{account}": {
      "get": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "Character roster for a linked account",
        "description": "",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "A game account linked to the caller."
          }
        ],
        "responses": {
          "200": {
            "description": "Account roster",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Account not linked to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Shard unavailable — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/shard/sales": {
      "get": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "Recent player-vendor sales for the caller’s linked accounts",
        "description": "",
        "responses": {
          "200": {
            "description": "Vendor sales",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardVendorSale"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/player/shard/vendors/{account}": {
      "get": {
        "tags": [
          "Player · Shard"
        ],
        "summary": "Player vendors for a linked account",
        "description": "",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "A game account linked to the caller."
          }
        ],
        "responses": {
          "200": {
            "description": "Vendor snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Account not linked to the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Shard unavailable — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/public/atlas/champions": {
      "get": {
        "tags": [
          "Public · Atlas"
        ],
        "summary": "Configured champion altars (the roster, not the live board)",
        "description": "Where the altars are and what each one summons — \"there is an Unholy Terror altar in Deceit\". `randomType` marks altars whose champion is drawn at activation. Do not conflate this with GET /public/shard/champs, which is the live sidecar-fed board (\"it is on level 3 right now\").",
        "parameters": [
          {
            "name": "facet",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Limit to one facet."
          }
        ],
        "responses": {
          "200": {
            "description": "Altars, by facet then name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AtlasChampion"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/atlas/creatures": {
      "get": {
        "tags": [
          "Public · Atlas"
        ],
        "summary": "Search the bestiary (paginated)",
        "description": "Every creature the shard spawns, most numerous first. `total` is how many can be alive at once across all spawners; `points` is how many spawners mention it; `facets` maps facet name to that creature\\'s share on it. Static content parsed from the shard\\'s ServUO tree — unaffected by the shard being offline.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Substring match on the creature name (max 60 chars)."
          },
          {
            "name": "facet",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Limit to creatures spawning on this facet. Facet names come from the shard's own files; an unknown one returns an empty page."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Page size, 1..100 (default 50)."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Rows to skip (default 0)."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of creatures plus the unpaginated total",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasCreaturePage"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "The atlas feature is gated above this caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The atlas feature is disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/atlas/creatures/{slug}": {
      "get": {
        "tags": [
          "Public · Atlas"
        ],
        "summary": "One creature: where it spawns, and what spawns with it",
        "description": "The answer the atlas exists to give. `places` is the aggregate — \"lizardman → Shrines, Isamu-Jima, Yew\" — resolved by point-in-rect against the shard\\'s own region rectangles, falling back to the nearest landmark, else \"Wilderness\". `spawners` lists the individual spawn points (bounded; `spawnersTruncated` says when the list was cut), and `alsoHere` is what shares those spawners.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Creature slug, e.g. lizardman."
          },
          {
            "name": "facet",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict places and spawners to one facet."
          },
          {
            "name": "points",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Max spawners to return, 1..1000 (default 200)."
          }
        ],
        "responses": {
          "200": {
            "description": "The creature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasCreature"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "No such creature in this atlas (or the feature is disabled)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/atlas/landmarks": {
      "get": {
        "tags": [
          "Public · Atlas"
        ],
        "summary": "Points of interest (dungeon levels, town markers)",
        "description": "From the shard\\'s Data/Locations files. `group` is the innermost enclosing parent (\"Covetous\"), which is the label worth showing over the individual marker (\"Level 1\").",
        "parameters": [
          {
            "name": "facet",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Limit to one facet."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Substring match on the landmark name or its group."
          }
        ],
        "responses": {
          "200": {
            "description": "Landmarks, by facet then group",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AtlasLandmark"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/atlas/meta": {
      "get": {
        "tags": [
          "Public · Atlas"
        ],
        "summary": "What atlas is loaded: facets, counts, when it was imported",
        "description": "Drives the facet filter and the \"parsed from the shard\\'s own files on <date>\" line. Reports the game world only — the ServUO path, the per-file hashes and any pending refresh are operator detail and live on the admin status route.",
        "responses": {
          "200": {
            "description": "Atlas metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtlasMeta"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/atlas/regions": {
      "get": {
        "tags": [
          "Public · Atlas"
        ],
        "summary": "Named regions and their rectangles",
        "description": "Flattened out of the shard\\'s nested Regions.xml. `priority` and the rectangles are what placed each spawn point, kept so the placement can be re-derived rather than taken on trust.",
        "parameters": [
          {
            "name": "facet",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Limit to one facet."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Substring match on the region name."
          }
        ],
        "responses": {
          "200": {
            "description": "Regions, by facet then name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AtlasRegion"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/contact": {
      "post": {
        "tags": [
          "Public"
        ],
        "summary": "Send a contact message",
        "description": "Emails the site owner (or falls back to a mailto). Rate limited.",
        "responses": {
          "200": {
            "description": "Message sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "429": {
            "description": "Too many messages (rate limited)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Mail delivery failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/public/pages/{id}/preview/{token}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Render a page from a draft-preview token",
        "description": "",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Page id."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Preview token from POST /admin/pages/:id/preview."
          }
        ],
        "responses": {
          "200": {
            "description": "The page (any status)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "Token invalid/expired or page missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/pages/{slug}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Get a published CMS page by slug",
        "description": "Drafts 404 for the public; staff sessions see drafts. Gated by site mode.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "The page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/posts/{category}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "List published posts in a category",
        "description": "Gated by site mode: during maintenance only admins with a valid session see content.",
        "parameters": [
          {
            "name": "category",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "news | five-on-friday | newsletter | screenshots"
          }
        ],
        "responses": {
          "200": {
            "description": "Published posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Post"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown category",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/posts/{category}/{idOrSlug}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Get a single published post",
        "description": "",
        "parameters": [
          {
            "name": "category",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Post category."
          },
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Numeric id or slug."
          }
        ],
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "404": {
            "description": "Unknown category or post not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/settings": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Public site settings + branding",
        "description": "Whitelisted, non-sensitive settings plus the per-shard brand block (name/colors/logo/hero/favicon) a client themes itself from, and derived registration / game-account-signup availability flags.",
        "responses": {
          "200": {
            "description": "Public settings + branding",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSettings"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/champs": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Current champion-spawn board (all categories)",
        "description": "The live board of every champion / mini-champ / sea-boss spawn. Update in place via the champ.update / champ.remove frames on /shard/stream.",
        "responses": {
          "200": {
            "description": "Champion spawns, ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/economy": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Gold-supply time series (oldest → newest)",
        "description": "",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Max samples (default 100, max 1000)."
          }
        ],
        "responses": {
          "200": {
            "description": "Economy samples",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardEconomyPoint"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/features": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Shard features visible to the caller (drives client nav)",
        "description": "The caller\\'s audience rung plus the shard features they may reach, so a client can hide nav entries instead of rendering links that 403. Reports only what the caller can see — the list itself does not disclose gated features.",
        "responses": {
          "200": {
            "description": "Visible features",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardFeatures"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/feed": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Recent notable shard events (from the ingested log)",
        "description": "The stored-history twin of /shard/stream, and it reaches the same verdict: which kinds are returned is resolved against the caller\\'s audience rung under the live visibility config, and each event\\'s payload is field-projected against its own kind\\'s feature. Kinds the caller may not read are omitted (an explicit ?kind= for one of them returns []), and acct/webId never appear below admin.",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter to a single event kind, e.g. vendor.sale. Returns [] if the caller may not read that kind."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Max rows (default 100, max 1000)."
          }
        ],
        "responses": {
          "200": {
            "description": "Events, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardEvent"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/governors": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Current town-governor board (City Loyalty)",
        "description": "One entry per city with its governor and election phase. Empty if the shard does not run the City Loyalty system. Live via city.update on /shard/stream.",
        "responses": {
          "200": {
            "description": "Cities, ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/governors/{city}/history": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Governor term history for a city",
        "description": "",
        "parameters": [
          {
            "name": "city",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City name, e.g. Britain."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Max terms (default 100, max 500)."
          }
        ],
        "responses": {
          "200": {
            "description": "Terms, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/guilds": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Current guild board (rosters, alliances, leaders)",
        "description": "The live board of every guild. Update in place via the guild.update / guild.remove / guild.join frames on /shard/stream.",
        "responses": {
          "200": {
            "description": "Guilds, ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/houses": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "House registry (owner, co-owners, price, decay)",
        "description": "Every house seen via the house.update registry feed. `price` is the placement value, not a for-sale flag. Live via house.update / house.remove on /shard/stream.",
        "responses": {
          "200": {
            "description": "Houses, ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardHouse"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/idoc": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Houses currently in danger (IDOC)",
        "description": "Location-level board of the houses about to collapse. Owner identity and price are gated by the `houses` feature\\'s field rules (default `staff`), and the owner\\'s game account is admin-only always — so an anonymous caller sees name, region and coordinates only.",
        "responses": {
          "200": {
            "description": "IDOC houses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardHouse"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/market": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Search the player-vendor marketplace",
        "description": "Every priced listing on every player vendor the shard publishes — the same index the in-game Vendor Search gump reads, and it honours the same per-vendor opt-out, so a player who hid their shop in game is hidden here too. Results are LISTINGS, each carrying enough of its shop to be actionable. Served from the site\\'s own tables (the sidecar is not touched), so it renders while the shard is down; `staleAt` is the oldest vendor row and the page must say how far behind the index can be — the shard sweeps vendors round-robin, so prices are inherently up to one full cycle old. Item names are resolved server-side against the cliloc table (docs/website/CLILOCS.md); on a shard that has not configured one, `displayName` is null and clients render the item id.",
        "parameters": [
          {
            "name": "itemId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "map",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "region",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Substring match on the resolved item name or the item's own literal name (max 60 chars)."
          },
          {
            "name": "minPrice",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Lowest price to include."
          },
          {
            "name": "maxPrice",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Highest price to include."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of listings plus the unpaginated total and the staleness stamp",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardMarketPage"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "The market feature is gated above this caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The market feature is disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/market/meta": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Marketplace size, staleness and filter options",
        "description": "How many vendors and listings the index holds, how stale it may be (`staleAt` = the oldest vendor row, `freshAt` = the newest), and which facets and regions actually hold vendors — so a client can build its filters without running a search it will discard.",
        "responses": {
          "200": {
            "description": "Marketplace metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardMarketMeta"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/market/vendors/{serial}": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "One player vendor and everything it is selling",
        "description": "A single shop by its vendor serial, with its listings. `truncated` (and `total` exceeding `count`) means the shop holds more than the shard publishes per frame — a commodity reseller with thousands of stacks is a real thing, and the page says so rather than presenting a partial shop as complete. Returns 404 for a serial the index has never seen, which also covers a vendor since dismissed or hidden.",
        "parameters": [
          {
            "name": "serial",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Vendor serial, e.g. 0x40001234"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Listings to return, 1..500 (default 250)."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Listings to skip (default 0)."
          }
        ],
        "responses": {
          "200": {
            "description": "The vendor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardMarketVendor"
                }
              }
            }
          },
          "400": {
            "description": "Malformed vendor serial"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "No such vendor in the index"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/online": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Staff online now (linked staff accounts; location is admin/moderator-only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Online players",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardOnlinePlayer"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/points": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Points / loyalty leaderboards, one board per point system",
        "description": "Every points/loyalty leaderboard the shard publishes (Queen\\'s Loyalty, Void Pool, the nine city loyalties, Clean Up Britannia, …), each with its display name, max points, participant count and top N. Served from our own store, so it renders while the shard is down; live via points.board on /shard/stream. A board\\'s display name may arrive as a literal (`nameString`) or a cliloc id (`nameNumber`) — resolve clilocs client-side.",
        "responses": {
          "200": {
            "description": "Boards, ordered by display name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardPointsBoard"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/points/{system}": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "One points system\\'s leaderboard",
        "description": "A single board by the shard\\'s own PointsType name (e.g. `QueensLoyalty`, `CleanUpBritannia`). Returns 404 when the shard has never published that system — distinct from a published board that nobody has scored in yet, which returns 200 with an empty `top`.",
        "parameters": [
          {
            "name": "system",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "PointsType name, e.g. QueensLoyalty"
          }
        ],
        "responses": {
          "200": {
            "description": "The board",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardPointsBoard"
                }
              }
            }
          },
          "400": {
            "description": "Malformed system name"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "The shard has never published that system"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/presence": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Online population aggregate (count + per-facet + per-region)",
        "description": "The latest presence.online snapshot powering the \"Players Online\" widget. Live via presence.online on /shard/stream.",
        "responses": {
          "200": {
            "description": "Population snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/ruleset": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "The shard\\'s published ruleset (expansion, systems, caps, limits)",
        "description": "How this shard is actually configured, published by the shard itself as one world.ruleset frame: expansion, which optional systems are on, skill/stat caps, account and house limits, champion scroll rules and the save/restart schedule. Served from our own store, so it renders while the shard is down; live via world.ruleset on /shard/stream. Returns `null` if the shard has never published one (an older plugin, or Bridge.RulesetEnabled=false) — distinct from a published ruleset, and the page renders it differently.",
        "responses": {
          "200": {
            "description": "The ruleset, or null if never published",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "nullable": true,
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/status": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Shard connection state, online count and latest economy",
        "description": "",
        "responses": {
          "200": {
            "description": "Shard status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShardStatus"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/stream": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Live shard event stream (Server-Sent Events, filtered by audience)",
        "description": "text/event-stream of live events. The caller\\'s audience rung is resolved once at subscribe time and frozen for the connection; each frame is then gated on its feature and field-projected, so sensitive kinds and fields (staff audit, cheat detection, login attempts, IPs, acct/webId) never reach a caller below their configured rung.",
        "responses": {
          "200": {
            "description": "An SSE stream (Content-Type: text/event-stream)."
          }
        }
      }
    },
    "/api/v1/public/status": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Site mode / status",
        "description": "Current site mode (live or maintenance) so the client can show the maintenance page, plus a version block (service id + API/server versions) for a client first-run probe and version-mismatch guard.",
        "responses": {
          "200": {
            "description": "Site status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicStatus"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/version": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Backend identity + version",
        "description": "Lightweight, DB-free descriptor of this backend: a stable service id and the API/server versions. A client uses it to recognize a Runic Gateway backend on first-run and to run a version-mismatch guard. Doubles as a cheap liveness check.",
        "responses": {
          "200": {
            "description": "Backend version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicVersion"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/wiki": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "List published wiki pages",
        "description": "",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published wiki pages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WikiPage"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/wiki/categories": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "List wiki categories",
        "description": "",
        "responses": {
          "200": {
            "description": "Wiki categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WikiCategory"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/wiki/tags": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "List wiki tags",
        "description": "",
        "responses": {
          "200": {
            "description": "Wiki tags",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/public/wiki/{slug}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Get a single published wiki page",
        "description": "",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Wiki page slug."
          }
        ],
        "responses": {
          "200": {
            "description": "The wiki page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WikiPage"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          },
          "503": {
            "description": "Service Unavailable"
          }
        }
      }
    },
    "/api/v1/settings/nav": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Nav overrides for the admin and player layouts",
        "description": "Returns the stored nav_admin and nav_player overrides as raw JSON strings (null when the admin never overrode that nav). Any authenticated account may read them: AdminLayout renders for editors and moderators, PlayerPortalLayout for players, and none of them can read GET /admin/settings. Presentation-only — the role/feature filters in the layouts still decide what is actually shown.",
        "responses": {
          "200": {
            "description": "Nav overrides",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NavSettings"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/api/v1/settings/theme/options": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Theme presets and the curated option lists",
        "description": "The closed sets an admin may choose from when theming the site: the three presets (with swatch colors), the curated Google Fonts shortlist per role, the shadow depths, and the editable color/radius field names. Served so the admin form can never offer a value the server would reject. Static — no database read.",
        "responses": {
          "200": {
            "description": "Theme option catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThemeOptions"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "rg_token",
        "description": "Session JWT set as an httpOnly cookie by POST /api/v1/auth/login."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Access token from POST /api/v1/auth/mobile/login (or /refresh)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "message": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Not found"
                  }
                }
              }
            }
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "errors": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "field"
                              }
                            }
                          },
                          "msg": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "Invalid value"
                              }
                            }
                          },
                          "path": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "username"
                              }
                            }
                          },
                          "location": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "body"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SafeUser": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              },
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "role": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "admin",
                      "editor"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              }
            }
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "username",
              "password"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "password": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  },
                  "example": {
                    "type": "string",
                    "example": "super-secret"
                  }
                }
              },
              "company": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Honeypot — must be empty for humans."
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              }
            }
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "username",
              "password"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 3
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 32
                  },
                  "example": {
                    "type": "string",
                    "example": "newplayer"
                  }
                }
              },
              "password": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 8
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 64
                  }
                }
              },
              "email": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "email"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "player@example.com"
                  }
                }
              },
              "company": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Honeypot — must be empty for humans."
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              }
            }
          }
        }
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Either a session (user) or, for 2FA accounts, a TOTP challenge to complete at /login/totp."
          },
          "properties": {
            "type": "object",
            "properties": {
              "user": {
                "$ref": "#/components/schemas/SafeUser"
              },
              "totpRequired": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "challenge": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Signed challenge token for the TOTP step."
                  }
                }
              }
            }
          }
        }
      },
      "TotpLoginRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "challenge"
            ],
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string",
            "example": "Second step for 2FA login. Supply either code OR recoveryCode."
          },
          "properties": {
            "type": "object",
            "properties": {
              "challenge": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Token returned by /login when totpRequired."
                  }
                }
              },
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Current authenticator code."
                  },
                  "example": {
                    "type": "string",
                    "example": "123456"
                  }
                }
              },
              "recoveryCode": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "A single-use recovery code (alternative to code)."
                  },
                  "example": {
                    "type": "string",
                    "example": "abcde-12345"
                  }
                }
              },
              "trustDevice": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Remember this browser so future logins skip the TOTP step (30 days)."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "deviceName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Optional friendly label for the Trusted Devices list."
                  },
                  "example": {
                    "type": "string",
                    "example": "My Laptop"
                  }
                }
              }
            }
          }
        }
      },
      "MobileLoginRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "username",
              "password"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "password": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  },
                  "example": {
                    "type": "string",
                    "example": "super-secret"
                  }
                }
              },
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "TOTP code (only when 2FA is enabled)."
                  },
                  "example": {
                    "type": "string",
                    "example": "123456"
                  }
                }
              },
              "recoveryCode": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Single-use recovery code (alternative to code)."
                  },
                  "example": {
                    "type": "string",
                    "example": "abcde-12345"
                  }
                }
              },
              "trustDevice": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Remember this device so future logins skip the TOTP step; the response then carries trustToken."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "device_name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Optional friendly device label for Active/Trusted Devices."
                  },
                  "example": {
                    "type": "string",
                    "example": "Pixel 8"
                  }
                }
              }
            }
          }
        }
      },
      "MobileTokenResponse": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "accessToken": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Short-lived bearer JWT."
                  }
                }
              },
              "refreshToken": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Long-lived, revocable refresh token."
                  }
                }
              },
              "expiresIn": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Access token lifetime as a duration string (zeit/ms format, e.g. \"15m\")."
                  },
                  "example": {
                    "type": "string",
                    "example": "15m"
                  }
                }
              },
              "user": {
                "$ref": "#/components/schemas/SafeUser"
              },
              "trustToken": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Present only when trustDevice was requested and accepted — store securely and send as X-Trust-Token on future logins to skip TOTP."
                  }
                }
              },
              "trustLimitReached": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Present (true) when trustDevice was requested but the device cap is reached; see devices."
                  }
                }
              },
              "devices": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "items": {
                    "$ref": "#/components/schemas/TrustedDevice"
                  },
                  "description": {
                    "type": "string",
                    "example": "The existing trusted devices, when trustLimitReached is set."
                  }
                }
              }
            }
          }
        }
      },
      "MobileRefreshRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "refreshToken"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "refreshToken": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              }
            }
          }
        }
      },
      "MobileLogoutRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "refreshToken": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Revoke a single session."
                  }
                }
              },
              "all": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Revoke every session for the user."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              }
            }
          }
        }
      },
      "MobileSsoExchangeRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "code",
              "code_verifier"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "The single-use authorization code returned to the app callback."
                  }
                }
              },
              "code_verifier": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "The PKCE verifier for the challenge sent to /auth/mobile/sso/start."
                  }
                }
              },
              "device_name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Optional friendly device label for Active Devices."
                  },
                  "example": {
                    "type": "string",
                    "example": "Pixel 8"
                  }
                }
              }
            }
          }
        }
      },
      "DeviceSession": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Session row id (pass to DELETE /auth/me/sessions/:id)."
                  }
                }
              },
              "deviceName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Pixel 8"
                  }
                }
              },
              "userAgent": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "createdAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "lastUsedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "expiresAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "TrustedDevice": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Trusted-device id (pass to DELETE …/trusted-devices/:id)."
                  }
                }
              },
              "platform": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "web",
                      "mobile"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "web"
                  }
                }
              },
              "deviceName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "My Laptop"
                  }
                }
              },
              "userAgent": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "createdAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "lastUsedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "expiresAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "TrustDeviceResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "trusted": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "trustToken": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Native clients only — store securely and send as X-Trust-Token."
                  }
                }
              }
            }
          }
        }
      },
      "TrustedDeviceLimit": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "error": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "trusted_device_limit"
                  }
                }
              },
              "devices": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "$ref": "#/components/schemas/TrustedDevice"
                  }
                }
              }
            }
          }
        }
      },
      "RecoveryCodes": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "recoveryCodes": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      },
                      "example": {
                        "type": "string",
                        "example": "abcde-12345"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Message": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "message": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Logged out."
                  }
                }
              }
            }
          }
        }
      },
      "ContactRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "message"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "message": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 5000
                  },
                  "example": {
                    "type": "string",
                    "example": "When does the shard launch?"
                  }
                }
              },
              "email": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "email"
                  },
                  "example": {
                    "type": "string",
                    "example": "player@example.com"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 100
                  },
                  "example": {
                    "type": "string",
                    "example": "Lord British"
                  }
                }
              }
            }
          }
        }
      },
      "Provider": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "google"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Google"
                  }
                }
              },
              "icon": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Icon hint — the provider kind ('google' | 'discord' | 'oidc' | 'oauth2')."
                  },
                  "example": {
                    "type": "string",
                    "example": "google"
                  }
                }
              },
              "loginUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Relative URL to begin the redirect flow."
                  },
                  "example": {
                    "type": "string",
                    "example": "/api/v1/auth/sso/google/start"
                  }
                }
              },
              "priority": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Sort order (ascending)."
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              }
            }
          }
        }
      },
      "ProviderConfig": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "okta"
                  }
                }
              },
              "kind": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "google",
                      "discord",
                      "oidc",
                      "oauth2"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "oidc"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Okta"
                  }
                }
              },
              "enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "clientId": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "hasSecret": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Whether a client secret is stored (the secret itself is never returned)."
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "authorizeUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  }
                }
              },
              "tokenUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  }
                }
              },
              "userinfoUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  }
                }
              },
              "scopes": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "openid email profile"
                  }
                }
              },
              "priority": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 10
                  }
                }
              },
              "builtin": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "True for the fixed google/discord providers."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "health": {
                "$ref": "#/components/schemas/ProviderHealth"
              }
            }
          }
        }
      },
      "ProviderHealth": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Config-completeness check that gates whether a provider is offered to end users."
          },
          "properties": {
            "type": "object",
            "properties": {
              "valid": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "missing": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "Names of required config fields that are still missing."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [],
                    "items": {}
                  }
                }
              }
            }
          }
        }
      },
      "ProviderCreateRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "id",
              "kind",
              "name"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "pattern": {
                    "type": "string",
                    "example": "^[a-z0-9-]+$"
                  },
                  "example": {
                    "type": "string",
                    "example": "okta"
                  }
                }
              },
              "kind": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "oidc",
                      "oauth2"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "oidc"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 80
                  },
                  "example": {
                    "type": "string",
                    "example": "Okta"
                  }
                }
              },
              "enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "clientId": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "secret": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  }
                }
              },
              "authorizeUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  }
                }
              },
              "tokenUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  }
                }
              },
              "userinfoUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  }
                }
              },
              "scopes": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 500
                  },
                  "example": {
                    "type": "string",
                    "example": "openid email profile"
                  }
                }
              },
              "priority": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 10
                  }
                }
              }
            }
          }
        }
      },
      "Post": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 12
                  }
                }
              },
              "category": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "news"
                  }
                }
              },
              "title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Server maintenance this weekend"
                  }
                }
              },
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "server-maintenance-this-weekend"
                  }
                }
              },
              "excerpt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "body": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "image_url": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "/uploads/1700000000-abcd.png"
                  }
                }
              },
              "published": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "author_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              },
              "created_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "updated_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "published_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "PostCreateRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "category",
              "title"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "category": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "news"
                  }
                }
              },
              "title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 200
                  },
                  "example": {
                    "type": "string",
                    "example": "Server maintenance this weekend"
                  }
                }
              },
              "body": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "image_url": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Required for the screenshots category."
                  }
                }
              },
              "published": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              }
            }
          }
        }
      },
      "PublishRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "published"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "published": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "UploadResponse": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "url": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "/uploads/1700000000-abcd.png"
                  }
                }
              }
            }
          }
        }
      },
      "WikiPage": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 3
                  }
                }
              },
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "getting-started"
                  }
                }
              },
              "title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Getting Started"
                  }
                }
              },
              "excerpt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "body": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "category_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 2
                  }
                }
              },
              "published": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "tags": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "newbie",
                      "guide"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "created_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "updated_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "WikiPageCreateRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "slug",
              "title"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "pattern": {
                    "type": "string",
                    "example": "^[a-z0-9-]+$"
                  },
                  "example": {
                    "type": "string",
                    "example": "getting-started"
                  }
                }
              },
              "title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 200
                  },
                  "example": {
                    "type": "string",
                    "example": "Getting Started"
                  }
                }
              },
              "excerpt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 400
                  }
                }
              },
              "body": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "category_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "published": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "tags": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "WikiCategory": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 2
                  }
                }
              },
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "guides"
                  }
                }
              },
              "title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Guides"
                  }
                }
              },
              "description": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "sort_order": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      },
      "WikiCategoryCreateRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "slug",
              "title"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "pattern": {
                    "type": "string",
                    "example": "^[a-z0-9-]+$"
                  },
                  "example": {
                    "type": "string",
                    "example": "guides"
                  }
                }
              },
              "title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 200
                  },
                  "example": {
                    "type": "string",
                    "example": "Guides"
                  }
                }
              },
              "description": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 400
                  }
                }
              },
              "sort_order": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              },
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "role": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "admin",
                      "editor",
                      "moderator",
                      "player"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "active",
                      "disabled",
                      "banned",
                      "pending"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "active"
                  }
                }
              },
              "email": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "email"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "email_verified": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "totp_enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "last_login_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "created_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "UserCreateRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "username",
              "password"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 3
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 32
                  },
                  "example": {
                    "type": "string",
                    "example": "editor1"
                  }
                }
              },
              "password": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 8
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 64
                  }
                }
              },
              "role": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "admin",
                      "editor",
                      "moderator",
                      "player"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "editor"
                  }
                }
              },
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "active",
                      "disabled",
                      "banned",
                      "pending"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "active"
                  }
                }
              },
              "email": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "email"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "ChangeUsernameRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "username"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 3
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 32
                  },
                  "example": {
                    "type": "string",
                    "example": "newname"
                  }
                }
              }
            }
          }
        }
      },
      "ChangePasswordRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "newPassword"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "newPassword": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 8
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 64
                  }
                }
              },
              "currentPassword": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "password"
                  },
                  "description": {
                    "type": "string",
                    "example": "Required when the account already has a password. Omit only for an SSO-provisioned account setting its first password."
                  }
                }
              }
            }
          }
        }
      },
      "PlayerAccount": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Self-service player account (GET /player/account)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 42
                  }
                }
              },
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "newplayer"
                  }
                }
              },
              "role": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "player"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "player"
                  }
                }
              },
              "email": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "email"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "player@example.com"
                  }
                }
              },
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "active",
                      "disabled",
                      "banned",
                      "pending"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "active"
                  }
                }
              },
              "totp_enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "has_password": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "False for an SSO-provisioned account that has not set a password yet."
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "OkFlag": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "ok": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "RegisterDeviceRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "endpoint"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "endpoint": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "uri"
                  },
                  "description": {
                    "type": "string",
                    "example": "The UnifiedPush/ntfy endpoint URL the distributor handed the app (or an FCM token). Must be an allowed HTTPS relay origin — private/loopback hosts are rejected."
                  },
                  "example": {
                    "type": "string",
                    "example": "https://ntfy.example.com/UP0a1b2c3d4e5f"
                  }
                }
              },
              "transport": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "unifiedpush",
                      "fcm"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "default": {
                    "type": "string",
                    "example": "unifiedpush"
                  },
                  "example": {
                    "type": "string",
                    "example": "unifiedpush"
                  }
                }
              },
              "platform": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 40
                  },
                  "example": {
                    "type": "string",
                    "example": "android"
                  }
                }
              }
            }
          }
        }
      },
      "PushDevice": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 7
                  }
                }
              },
              "transport": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "unifiedpush",
                      "fcm"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "unifiedpush"
                  }
                }
              },
              "endpoint": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "https://ntfy.example.com/UP0a1b2c3d4e5f"
                  }
                }
              },
              "platform": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "android"
                  }
                }
              },
              "createdAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "lastSeenAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "NotificationStream": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "One subscribable push stream from the catalog."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "idoc.warning"
                  }
                }
              },
              "label": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "IDOC warnings"
                  }
                }
              },
              "description": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "A house falls into its final (IDOC) decay stage."
                  }
                }
              },
              "personal": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Owner-keyed — delivered only to the owning user, never fanned out publicly."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "requiresLinkedAccount": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "The stream needs a linked game account (personal streams)."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              }
            }
          }
        }
      },
      "NotificationStreams": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "streams": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "$ref": "#/components/schemas/NotificationStream"
                  }
                }
              }
            }
          }
        }
      },
      "NotificationSubscriptions": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "The set of stream ids the user has opted into (used for both GET and PUT)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "streams": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "news.post",
                      "idoc.warning",
                      "vendor.sale"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Appeal": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A player-submitted moderation appeal (as returned to the player and in the staff queue)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 12
                  }
                }
              },
              "mod_action_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 340
                  }
                }
              },
              "discord_user_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "216734083584917504"
                  }
                }
              },
              "action_type": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "ban",
                      "mute"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "ban"
                  }
                }
              },
              "user_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 42
                  }
                }
              },
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "pending",
                      "under_review",
                      "approved",
                      "denied",
                      "withdrawn"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "pending"
                  }
                }
              },
              "submitted_text": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "I was banned by mistake — please review."
                  }
                }
              },
              "staff_response": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {}
                }
              },
              "handled_by_user_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {}
                }
              },
              "handled_by_tag": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {}
                }
              },
              "reversal_status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "none",
                      "done",
                      "failed"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "Discord-reversal outcome. done/failed only after an approval; none otherwise."
                  },
                  "example": {
                    "type": "string",
                    "example": "none"
                  }
                }
              },
              "submitted_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "resolved_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {}
                }
              },
              "action_target_tag": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Rogue#1234"
                  },
                  "description": {
                    "type": "string",
                    "example": "Snapshot of the original action target tag (from mod_actions)."
                  }
                }
              },
              "action_reason": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Spam"
                  }
                }
              },
              "action_created_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "action_duration_seconds": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 86400
                  }
                }
              },
              "submitter_username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "newplayer"
                  }
                }
              }
            }
          }
        }
      },
      "AppealQueueItem": {
        "type": "object",
        "properties": {
          "allOf": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Appeal"
            }
          },
          "description": {
            "type": "string",
            "example": "A staff-queue appeal row — identical shape to Appeal, with the joined action/submitter columns populated."
          }
        }
      },
      "AppealResolveResult": {
        "type": "object",
        "properties": {
          "allOf": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "example": "object"
                },
                "properties": {
                  "type": "object",
                  "properties": {
                    "reversal": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "example": "object"
                        },
                        "description": {
                          "type": "string",
                          "example": "What the approval attempted against Discord."
                        },
                        "properties": {
                          "type": "object",
                          "properties": {
                            "attempted": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "example": "boolean"
                                },
                                "example": {
                                  "type": "boolean",
                                  "example": true
                                }
                              }
                            },
                            "ok": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "example": "boolean"
                                },
                                "example": {
                                  "type": "boolean",
                                  "example": true
                                }
                              }
                            },
                            "reversal_status": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "example": "string"
                                },
                                "enum": {
                                  "type": "array",
                                  "example": [
                                    "none",
                                    "done",
                                    "failed"
                                  ],
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "example": {
                                  "type": "string",
                                  "example": "done"
                                }
                              }
                            },
                            "bot_status": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "example": "integer"
                                },
                                "nullable": {
                                  "type": "boolean",
                                  "example": true
                                },
                                "example": {
                                  "type": "number",
                                  "example": 200
                                },
                                "description": {
                                  "type": "string",
                                  "example": "HTTP status from the bot internal call, or null when no call was made."
                                }
                              }
                            },
                            "error": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "example": "string"
                                },
                                "nullable": {
                                  "type": "boolean",
                                  "example": true
                                },
                                "example": {}
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AppealEligibleAction": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A ban/mute mod_action the caller may appeal (no active appeal outstanding)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 340
                  },
                  "description": {
                    "type": "string",
                    "example": "mod_action id — pass as mod_action_id when submitting."
                  }
                }
              },
              "action_type": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "ban",
                      "mute"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "ban"
                  }
                }
              },
              "target_tag": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Rogue#1234"
                  }
                }
              },
              "reason": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Spam"
                  }
                }
              },
              "duration_seconds": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 86400
                  }
                }
              },
              "created_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "CreateAppealRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "mod_action_id",
              "submitted_text"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "mod_action_id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 340
                  },
                  "description": {
                    "type": "string",
                    "example": "The ban/mute mod_action to appeal (must belong to the caller)."
                  }
                }
              },
              "submitted_text": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "minLength": {
                    "type": "number",
                    "example": 1
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 4000
                  },
                  "example": {
                    "type": "string",
                    "example": "I was banned by mistake — please review."
                  }
                }
              }
            }
          }
        }
      },
      "ResolveAppealRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "status"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "approved",
                      "denied"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "approved"
                  }
                }
              },
              "staff_response": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 4000
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Reviewed — reversing the ban."
                  }
                }
              }
            }
          }
        }
      },
      "TotpCodeRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "code"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "123456"
                  }
                }
              }
            }
          }
        }
      },
      "SiteModeRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "mode"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "live",
                      "maintenance"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "live"
                  }
                }
              }
            }
          }
        }
      },
      "UnbanRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "ip"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "ip": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "203.0.113.5"
                  }
                }
              }
            }
          }
        }
      },
      "AccountStatus": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Self-service account security status (GET /admin/account)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              },
              "username": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "role": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "admin",
                      "editor"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              },
              "totp_enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "TotpSetup": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Enrollment material returned by POST /account/totp/setup."
          },
          "properties": {
            "type": "object",
            "properties": {
              "otpauthUrl": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "otpauth://totp/Runic Gateway:admin?secret=..."
                  }
                }
              },
              "qr": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "QR code as a data: URL."
                  },
                  "example": {
                    "type": "string",
                    "example": "data:image/png;base64,iVBORw0KGgo..."
                  }
                }
              }
            }
          }
        }
      },
      "TotpState": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Result of enabling/disabling 2FA. Enabling also returns the one-time recovery codes."
          },
          "properties": {
            "type": "object",
            "properties": {
              "totp_enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "recoveryCodes": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Single-use recovery codes, shown ONCE on enable."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      },
                      "example": {
                        "type": "string",
                        "example": "abcde-12345"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "LinkedIdentity": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "google"
                  }
                }
              },
              "email": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "email"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "user@example.com"
                  }
                }
              },
              "linked_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "SiteModeState": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Result of PUT /admin/site-mode."
          },
          "properties": {
            "type": "object",
            "properties": {
              "site_mode": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "live",
                      "maintenance"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "maintenance"
                  }
                }
              },
              "changed_at": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "changed_by": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "admin"
                  }
                }
              }
            }
          }
        }
      },
      "PublicStatus": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Public site status (GET /public/status)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "live",
                      "maintenance"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "live"
                  }
                }
              },
              "status_message": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              },
              "version": {
                "$ref": "#/components/schemas/PublicVersion"
              }
            }
          }
        }
      },
      "PublicVersion": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Backend identity + version (GET /public/version; also embedded in /public/status)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "service": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "runic-gateway"
                  },
                  "description": {
                    "type": "string",
                    "example": "Stable backend identifier for first-run recognition."
                  }
                }
              },
              "api": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "v1"
                  },
                  "description": {
                    "type": "string",
                    "example": "API contract version (matches the /api/v1 mount)."
                  }
                }
              },
              "server": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "1.0.0"
                  },
                  "description": {
                    "type": "string",
                    "example": "Server package version (informational)."
                  }
                }
              }
            }
          }
        }
      },
      "Brand": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Per-shard branding (BRAND_* env, with admin overrides for name/contactEmail). A client themes itself from this — one instance runs as any shard. Asset fields (logo/hero/favicon) may be site-relative paths; resolve them against the site base URL."
          },
          "properties": {
            "type": "object",
            "properties": {
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Runic Gateway"
                  }
                }
              },
              "shortName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Runic Gateway"
                  }
                }
              },
              "tagline": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "an independent private Ultima Online shard"
                  }
                }
              },
              "description": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  }
                }
              },
              "contactEmail": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              },
              "url": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              },
              "accent": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "#7f99bd"
                  },
                  "description": {
                    "type": "string",
                    "example": "Seed/accent color (hex) for theming. **Effective** value: the admin theme (theme_visual) wins over BRAND_ACCENT_COLOR, so a client that themes from this tracks admin theming with no change."
                  }
                }
              },
              "logo": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  },
                  "description": {
                    "type": "string",
                    "example": "Logo URL or site-relative path; empty = no logo. An uploaded brand_assets.logo overrides BRAND_LOGO."
                  }
                }
              },
              "hero": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "/assets/img/runic-emblem.png"
                  },
                  "description": {
                    "type": "string",
                    "example": "Hero image URL or site-relative path. An uploaded brand_assets.hero overrides BRAND_HERO."
                  }
                }
              },
              "favicon": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "/assets/img/favicon.ico"
                  },
                  "description": {
                    "type": "string",
                    "example": "Favicon URL or site-relative path. An uploaded brand_assets.favicon overrides BRAND_FAVICON."
                  }
                }
              }
            }
          }
        }
      },
      "PublicSettings": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Public site settings + branding (GET /public/settings). Whitelisted string settings, plus derived availability flags and the brand block a client themes from. Additional whitelisted keys may appear."
          },
          "properties": {
            "type": "object",
            "properties": {
              "site_title": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Runic Gateway"
                  }
                }
              },
              "status_message": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              },
              "maintenance_message": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": ""
                  }
                }
              },
              "registration": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "properties": {
                    "type": "object",
                    "properties": {
                      "password": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "boolean"
                          }
                        }
                      },
                      "sso": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "boolean"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "gameAccountSignup": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "brand": {
                "$ref": "#/components/schemas/Brand"
              },
              "theme": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The effective CSS custom properties for the admin theme, resolved server-side (:root ← preset ← custom). **Absent** when the admin never set a theme, which is what makes an untouched instance render from the shipped stylesheet unchanged. Keys are CSS variable names; every value comes from a closed set (hex color, curated font stack, bounded px length, listed shadow)."
                  },
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "object",
                    "properties": {
                      "--accent": {
                        "type": "string",
                        "example": "#c9973f"
                      },
                      "--bg": {
                        "type": "string",
                        "example": "#1a120b"
                      },
                      "--radius-card": {
                        "type": "string",
                        "example": "2px"
                      }
                    }
                  }
                }
              },
              "push": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "description": {
                    "type": "string",
                    "example": "Push-notification relay config (M7). `ntfyUrl` is the client-facing ntfy base URL the app registers its device topic against (from NTFY_PUBLIC_URL / NTFY_ALLOWED_ORIGINS); null when push is not configured for this shard."
                  },
                  "properties": {
                    "type": "object",
                    "properties": {
                      "ntfyUrl": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "string"
                          },
                          "nullable": {
                            "type": "boolean",
                            "example": true
                          },
                          "example": {
                            "type": "string",
                            "example": "https://ntfy.example.com"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "additionalProperties": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "NavSettings": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Nav overrides for the two authenticated layouts (GET /settings/nav). Each value is the stored JSON **string** — settings.value is TEXT — or null when that nav was never overridden. Parse fail-safe: treat malformed as absent and fall back to the hardcoded nav."
          },
          "properties": {
            "type": "object",
            "properties": {
              "nav_admin": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "{\"/admin/posts\":{\"label\":\"Blog Posts\",\"order\":10}}"
                  }
                }
              },
              "nav_player": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {}
                }
              }
            }
          }
        }
      },
      "ThemeOptions": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "The closed sets an admin may choose from when theming the site (GET /settings/theme-options). Served so the admin form cannot offer a value PUT /admin/settings would reject. Static — derived from the server theme config, not the database."
          },
          "properties": {
            "type": "object",
            "properties": {
              "presets": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "Selectable presets and their full token maps, so a form can show what an unset field currently resolves to. `custom` has null tokens and means \"no preset base — the shipped theme plus whatever custom fields are set\"."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "fantasy"
                              }
                            }
                          },
                          "label": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "Fantasy"
                              }
                            }
                          },
                          "tokens": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "object"
                              },
                              "nullable": {
                                "type": "boolean",
                                "example": true
                              },
                              "additionalProperties": {
                                "type": "object",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "example": "string"
                                  }
                                }
                              },
                              "example": {
                                "type": "object",
                                "properties": {
                                  "--bg": {
                                    "type": "string",
                                    "example": "#1a120b"
                                  },
                                  "--accent": {
                                    "type": "string",
                                    "example": "#c9973f"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "colorFields": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "Editable color fields, each paired with the CSS variable it drives."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "accent"
                              }
                            }
                          },
                          "token": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "--accent"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "radiusFields": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "radiusCard"
                              }
                            }
                          },
                          "token": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "--radius-card"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "shippedTokens": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "description": {
                    "type": "string",
                    "example": "What the stylesheet declares by default — the values an unset field resolves to when no preset is selected."
                  },
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  }
                }
              },
              "fonts": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "description": {
                    "type": "string",
                    "example": "Curated Google Fonts shortlist per role. Each option's `value` is the full CSS font-family stack exactly as it will be applied — the stored value, so no stack is ever built from admin input."
                  },
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "array"
                      },
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "object"
                          },
                          "properties": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "object",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "example": "string"
                                  }
                                }
                              },
                              "label": {
                                "type": "object",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "example": "string"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "shadows": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              }
                            }
                          },
                          "label": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "radiusMaxPx": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 999
                  }
                }
              }
            }
          }
        }
      },
      "DeletedId": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 12
                  }
                }
              }
            }
          }
        }
      },
      "DeletedSlug": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "getting-started"
                  }
                }
              }
            }
          }
        }
      },
      "DeletedFlag": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "deleted": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "UnlinkedFlag": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "unlinked": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "UnbanResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "ip": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "203.0.113.5"
                  }
                }
              },
              "removed": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Whether the IP had an entry that was cleared."
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "ShardStatus": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Public shard status (GET /public/shard/status)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "connected"
                  },
                  "description": {
                    "type": "string",
                    "example": "connected | reconnecting | disconnected | error"
                  }
                }
              },
              "pluginConnected": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Is the shard link up right now?"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "lastEventAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "onlineCount": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 12
                  }
                }
              },
              "economy": {
                "$ref": "#/components/schemas/ShardEconomyPoint"
              }
            }
          }
        }
      },
      "ShardEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A logged shard event."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 4821
                  }
                }
              },
              "kind": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "vendor.sale"
                  }
                }
              },
              "t": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Event time, epoch ms."
                  },
                  "example": {
                    "type": "number",
                    "example": 1783720195626
                  }
                }
              },
              "bootId": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "boot-abc123"
                  }
                }
              },
              "payload": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "additionalProperties": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The full event object."
                  }
                }
              },
              "createdAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "ShardEconomyPoint": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "nullable": {
            "type": "boolean",
            "example": true
          },
          "description": {
            "type": "string",
            "example": "One gold-supply sample."
          },
          "properties": {
            "type": "object",
            "properties": {
              "accounts": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 240
                  }
                }
              },
              "gold": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1028983421
                  }
                }
              },
              "t": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Sample time, epoch ms."
                  },
                  "example": {
                    "type": "number",
                    "example": 1783720000000
                  }
                }
              }
            }
          }
        }
      },
      "ShardOnlinePlayer": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A LINKED player online now (only accounts linked to a website user are listed)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "serial": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "0x24C"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Darrow"
                  }
                }
              },
              "map": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Trammel"
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1402
                  }
                }
              },
              "y": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1604
                  }
                }
              },
              "z": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      },
      "ShardVendorSale": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A player-vendor sale (visible only to the linked owner)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "t": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Sale time, epoch ms."
                  },
                  "example": {
                    "type": "number",
                    "example": 1783720195626
                  }
                }
              },
              "itemType": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Longsword"
                  }
                }
              },
              "amount": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              },
              "price": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 100
                  }
                }
              },
              "commission": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 5
                  }
                }
              },
              "ownerAcct": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "whitlocktech"
                  }
                }
              }
            }
          }
        }
      },
      "ShardHouse": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A house at its current decay stage."
          },
          "properties": {
            "type": "object",
            "properties": {
              "serial": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "0x4004705F"
                  }
                }
              },
              "stage": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "IDOC"
                  }
                }
              },
              "map": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Trammel"
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "y": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "z": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "region": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "An Unnamed House"
                  }
                }
              },
              "ownerSerial": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "ownerAcct": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "builtOn": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "lastRefreshed": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "isIdoc": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "updatedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "ShardPointsBoard": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "One point system's leaderboard (Protocol 3.0 points.board). The shard carries ~25 separate point currencies; each publishes its own board. The display name may arrive as a literal string, a cliloc id, or both — resolve clilocs client-side."
          },
          "properties": {
            "type": "object",
            "properties": {
              "system": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "QueensLoyalty"
                  },
                  "description": {
                    "type": "string",
                    "example": "The shard's PointsType name; the board's stable key."
                  }
                }
              },
              "nameString": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Queen's Loyalty"
                  }
                }
              },
              "nameNumber": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1114938
                  },
                  "description": {
                    "type": "string",
                    "example": "Cliloc id, 0 when the name is a literal."
                  }
                }
              },
              "maxPoints": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 30000
                  }
                }
              },
              "players": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 842
                  },
                  "description": {
                    "type": "string",
                    "example": "Players actually holding points in this system."
                  }
                }
              },
              "showOnGump": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The shard's own 'is this player-facing?' flag."
                  }
                }
              },
              "top": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "The ranked players, best first. Capped by the shard (10 by default). Empty when nobody has scored yet."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "rank": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "example": {
                                "type": "number",
                                "example": 1
                              }
                            }
                          },
                          "serial": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "0x1A2B"
                              }
                            }
                          },
                          "name": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "Darrow"
                              },
                              "description": {
                                "type": "string",
                                "example": "Omitted when the leaderboards `name` field is gated above the caller."
                              }
                            }
                          },
                          "points": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "example": {
                                "type": "number",
                                "example": 29500
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "t": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Frame time, epoch ms."
                  }
                }
              },
              "updatedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "ShardMarketLocation": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "nullable": {
            "type": "boolean",
            "example": true
          },
          "description": {
            "type": "string",
            "example": "Where a vendor is standing. ONE nested object rather than flat map/x/y/region because it is one admin-configurable field (`market.location`) — the whole object is omitted when that field is gated above the caller."
          },
          "properties": {
            "type": "object",
            "properties": {
              "map": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Trammel"
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1421
                  }
                }
              },
              "y": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1699
                  }
                }
              },
              "z": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              },
              "region": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Britain"
                  }
                }
              },
              "house": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Darrow's Villa"
                  },
                  "description": {
                    "type": "string",
                    "example": "The house SIGN's name, not the house type. Null for a vendor standing outside one."
                  }
                }
              }
            }
          }
        }
      },
      "ShardMarketListing": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "One priced listing on a player vendor, carrying enough of its shop to be actionable without a second request."
          },
          "properties": {
            "type": "object",
            "properties": {
              "serial": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "0x40012ABC"
                  }
                }
              },
              "itemId": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 3922
                  },
                  "description": {
                    "type": "string",
                    "example": "ItemID (the art/graphic id)."
                  }
                }
              },
              "hue": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              },
              "amount": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1
                  }
                }
              },
              "price": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 25000
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The item's own literal name, set by a player. Null for most items."
                  }
                }
              },
              "cliloc": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 1023721
                  },
                  "description": {
                    "type": "string",
                    "example": "The item's LabelNumber."
                  }
                }
              },
              "displayName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "quarter staff"
                  },
                  "description": {
                    "type": "string",
                    "example": "Resolved server-side from `name` (preferred, being player-set and more specific) else `cliloc`. Null on a shard with no cliloc table configured — render the item id."
                  }
                }
              },
              "child": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  },
                  "description": {
                    "type": "string",
                    "example": "Priced by an enclosing container rather than itself, exactly as the in-game Vendor Search reports it."
                  }
                }
              },
              "vendor": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "properties": {
                    "type": "object",
                    "properties": {
                      "serial": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "string"
                          },
                          "example": {
                            "type": "string",
                            "example": "0x40001234"
                          }
                        }
                      },
                      "shopName": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "string"
                          },
                          "nullable": {
                            "type": "boolean",
                            "example": true
                          },
                          "example": {
                            "type": "string",
                            "example": "Darrow's Bargains"
                          }
                        }
                      },
                      "ownerSerial": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "string"
                          },
                          "nullable": {
                            "type": "boolean",
                            "example": true
                          },
                          "example": {
                            "type": "string",
                            "example": "0x1A2B"
                          },
                          "description": {
                            "type": "string",
                            "example": "Omitted when the market `ownerSerial` field is gated above the caller."
                          }
                        }
                      },
                      "ownerName": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "string"
                          },
                          "nullable": {
                            "type": "boolean",
                            "example": true
                          },
                          "example": {
                            "type": "string",
                            "example": "Darrow"
                          },
                          "description": {
                            "type": "string",
                            "example": "Omitted when the market `ownerName` field is gated above the caller."
                          }
                        }
                      },
                      "location": {
                        "$ref": "#/components/schemas/ShardMarketLocation"
                      },
                      "updatedAt": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "example": "string"
                          },
                          "format": {
                            "type": "string",
                            "example": "date-time"
                          },
                          "description": {
                            "type": "string",
                            "example": "When the shard last published this shop."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ShardMarketPage": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A page of marketplace listings plus the unpaginated total and the staleness stamp."
          },
          "properties": {
            "type": "object",
            "properties": {
              "listings": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "$ref": "#/components/schemas/ShardMarketListing"
                  }
                }
              },
              "total": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1284
                  },
                  "description": {
                    "type": "string",
                    "example": "Matching listings, ignoring paging."
                  }
                }
              },
              "limit": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 50
                  }
                }
              },
              "offset": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              },
              "vendors": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 137
                  },
                  "description": {
                    "type": "string",
                    "example": "Vendors in the whole index."
                  }
                }
              },
              "staleAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The OLDEST vendor row. The shard sweeps vendors round-robin, so the index can be a full cycle behind and a client must say so rather than implying live prices."
                  }
                }
              }
            }
          }
        }
      },
      "ShardMarketVendor": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "One player vendor and its listings."
          },
          "properties": {
            "type": "object",
            "properties": {
              "serial": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "0x40001234"
                  }
                }
              },
              "shopName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Darrow's Bargains"
                  }
                }
              },
              "ownerSerial": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "ownerName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Darrow"
                  }
                }
              },
              "location": {
                "$ref": "#/components/schemas/ShardMarketLocation"
              },
              "count": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 250
                  },
                  "description": {
                    "type": "string",
                    "example": "Listings the shard published for this shop."
                  }
                }
              },
              "total": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 3104
                  },
                  "description": {
                    "type": "string",
                    "example": "Listings the shop actually holds."
                  }
                }
              },
              "truncated": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "`total` exceeds `count` — the shop holds more than the shard publishes per frame."
                  }
                }
              },
              "updatedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              },
              "items": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "$ref": "#/components/schemas/ShardMarketListing"
                  }
                }
              }
            }
          }
        }
      },
      "ShardMarketMeta": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Marketplace size, staleness and the filter options a client needs to build its UI."
          },
          "properties": {
            "type": "object",
            "properties": {
              "vendors": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 137
                  }
                }
              },
              "items": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 18422
                  }
                }
              },
              "staleAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "freshAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "maps": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "Felucca",
                      "Trammel"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "Facets that actually hold vendors. From the shard's own data — never a hardcoded list."
                  }
                }
              },
              "regions": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "Britain",
                      "Luna"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ShardFeatures": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "The shard features the caller may reach, plus the audience rung they resolved to. Drives client nav so it never renders a link that would 403."
          },
          "properties": {
            "type": "object",
            "properties": {
              "level": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "anonymous",
                      "logged_in",
                      "player",
                      "staff",
                      "admin"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "anonymous"
                  }
                }
              },
              "features": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "status",
                      "activity",
                      "champs",
                      "guilds",
                      "governors",
                      "houses",
                      "presence"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ShardFeatureVisibility": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Visibility settings for one shard feature."
          },
          "properties": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "audience": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "anonymous",
                      "logged_in",
                      "player",
                      "staff",
                      "admin"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "Minimum rung that may reach this feature. Each rung implies the ones below it."
                  },
                  "example": {
                    "type": "string",
                    "example": "anonymous"
                  }
                }
              },
              "stream": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "Whether this feature's event kinds fan out over SSE at all."
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "fieldRules": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "Per-field rung overrides for the sensitive fields this feature exposes. acct / webId are admin-only always and are rejected here."
                  },
                  "example": {
                    "type": "object",
                    "properties": {
                      "location": {
                        "type": "string",
                        "example": "staff"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ShardVisibilityConfig": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "ladder": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "anonymous",
                      "logged_in",
                      "player",
                      "staff",
                      "admin"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "lockedFields": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "acct",
                      "webId"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "defaults": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "additionalProperties": {
                    "$ref": "#/components/schemas/ShardFeatureVisibility"
                  }
                }
              },
              "features": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "additionalProperties": {
                    "$ref": "#/components/schemas/ShardFeatureVisibility"
                  }
                }
              }
            }
          }
        }
      },
      "ShardVisibilityUpdate": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "features"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "features": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "additionalProperties": {
                    "$ref": "#/components/schemas/ShardFeatureVisibility"
                  },
                  "example": {
                    "type": "object",
                    "properties": {
                      "market": {
                        "type": "object",
                        "properties": {
                          "enabled": {
                            "type": "boolean",
                            "example": true
                          },
                          "audience": {
                            "type": "string",
                            "example": "player"
                          },
                          "stream": {
                            "type": "boolean",
                            "example": false
                          },
                          "fieldRules": {
                            "type": "object",
                            "properties": {
                              "ownerName": {
                                "type": "string",
                                "example": "player"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AtlasCreature": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A creature in the bestiary. `places`/`points`/`alsoHere` are present only on the single-creature route."
          },
          "properties": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "lizardman"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Lizardman"
                  }
                }
              },
              "total": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "How many can be alive at once, summed across every spawner."
                  },
                  "example": {
                    "type": "number",
                    "example": 214
                  }
                }
              },
              "points": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "How many spawners mention this creature."
                  },
                  "example": {
                    "type": "number",
                    "example": 62
                  }
                }
              },
              "facets": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "integer"
                      }
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "This creature's share per facet."
                  },
                  "example": {
                    "type": "object",
                    "properties": {
                      "Felucca": {
                        "type": "number",
                        "example": 96
                      },
                      "Trammel": {
                        "type": "number",
                        "example": 88
                      },
                      "Tokuno": {
                        "type": "number",
                        "example": 30
                      }
                    }
                  }
                }
              },
              "art": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Operator-supplied art under uploads/atlas/. NULL on a fresh import — the repo ships no creature art."
                  }
                }
              },
              "places": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "Where it spawns, aggregated by resolved place. The answer the atlas exists to give."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "facet": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "Trammel"
                              }
                            }
                          },
                          "label": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "description": {
                                "type": "string",
                                "example": "Resolved region, else nearest landmark group, else \"Wilderness\"."
                              },
                              "example": {
                                "type": "string",
                                "example": "Shrines"
                              }
                            }
                          },
                          "spawners": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "example": {
                                "type": "number",
                                "example": 7
                              }
                            }
                          },
                          "maxAlive": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "example": {
                                "type": "number",
                                "example": 21
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "spawners": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "The individual spawners. Named separately from `points` (the count) so one key never means two things."
                  },
                  "items": {
                    "$ref": "#/components/schemas/AtlasSpawner"
                  }
                }
              },
              "spawnersTruncated": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "example": "True when the spawner list was cut at the requested bound."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "alsoHere": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "Creatures sharing a spawner with this one."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "lizardman-warrior"
                              }
                            }
                          },
                          "name": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "Lizardman Warrior"
                              }
                            }
                          },
                          "shared": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "example": {
                                "type": "number",
                                "example": 12
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AtlasSpawner": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "One ServUO spawner, with the place its coordinates resolved to."
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "facet": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Felucca"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The spawner's own name in the ServUO file."
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 5411
                  }
                }
              },
              "y": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1234
                  }
                }
              },
              "width": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "height": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "range": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Spawn radius."
                  }
                }
              },
              "maxCount": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "How many of THIS creature this spawner keeps alive."
                  },
                  "example": {
                    "type": "number",
                    "example": 3
                  }
                }
              },
              "minDelay": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Respawn window, in SECONDS. Normalised at parse time — the source stores minutes or seconds per record, decided by its own DelayInSec flag."
                  },
                  "example": {
                    "type": "number",
                    "example": 300
                  }
                }
              },
              "maxDelay": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 600
                  }
                }
              },
              "todStart": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Meaningless unless todMode is non-zero."
                  }
                }
              },
              "todEnd": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "todMode": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "region": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Despise"
                  }
                }
              },
              "landmark": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Covetous"
                  }
                }
              },
              "label": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "Region, else landmark group, else \"Wilderness\"."
                  },
                  "example": {
                    "type": "string",
                    "example": "Despise"
                  }
                }
              }
            }
          }
        }
      },
      "AtlasCreaturePage": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "total": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Matching creatures before pagination."
                  },
                  "example": {
                    "type": "number",
                    "example": 800
                  }
                }
              },
              "limit": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 50
                  }
                }
              },
              "offset": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              },
              "creatures": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "$ref": "#/components/schemas/AtlasCreature"
                  }
                }
              }
            }
          }
        }
      },
      "AtlasRegion": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A named region, flattened out of the shard's nested Regions.xml."
          },
          "properties": {
            "type": "object",
            "properties": {
              "facet": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Felucca"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Despise"
                  }
                }
              },
              "type": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "ServUO region class."
                  },
                  "example": {
                    "type": "string",
                    "example": "DungeonRegion"
                  }
                }
              },
              "priority": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 50
                  }
                }
              },
              "parent": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Britain"
                  }
                }
              },
              "rects": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "description": {
                    "type": "string",
                    "example": "The rectangles that placed each spawn point."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "additionalProperties": {
                        "type": "boolean",
                        "example": true
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AtlasLandmark": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "facet": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Trammel"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Level 1"
                  }
                }
              },
              "group": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Innermost enclosing parent — the label worth showing."
                  },
                  "example": {
                    "type": "string",
                    "example": "Covetous"
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 5411
                  }
                }
              },
              "y": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 1234
                  }
                }
              },
              "z": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      },
      "AtlasChampion": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A CONFIGURED champion altar. Not the live board — see GET /public/shard/champs for that."
          },
          "properties": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "felucca-deceit"
                  }
                }
              },
              "name": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Deceit"
                  }
                }
              },
              "group": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Spawn group; one altar active per group."
                  },
                  "example": {
                    "type": "string",
                    "example": "Dungeons"
                  }
                }
              },
              "type": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "NULL when the champion is drawn at activation."
                  },
                  "example": {
                    "type": "string",
                    "example": "UnholyTerror"
                  }
                }
              },
              "randomType": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "facet": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "Felucca"
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "y": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "z": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  }
                }
              },
              "radius": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 60
                  }
                }
              },
              "label": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Deceit"
                  }
                }
              }
            }
          }
        }
      },
      "AtlasMeta": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "What atlas is loaded. Game-world facts only: the ServUO path, source hashes and any pending refresh are operator detail and live on the admin status route."
          },
          "properties": {
            "type": "object",
            "properties": {
              "importedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "generatedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "counts": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "additionalProperties": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "object",
                    "properties": {
                      "facets": {
                        "type": "number",
                        "example": 6
                      },
                      "points": {
                        "type": "number",
                        "example": 6455
                      },
                      "creatures": {
                        "type": "number",
                        "example": 800
                      },
                      "regions": {
                        "type": "number",
                        "example": 387
                      },
                      "landmarks": {
                        "type": "number",
                        "example": 558
                      },
                      "champions": {
                        "type": "number",
                        "example": 25
                      },
                      "unresolvedPoints": {
                        "type": "number",
                        "example": 1086
                      }
                    }
                  }
                }
              },
              "facets": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "Felucca",
                      "Ilshenar",
                      "Malas",
                      "TerMur",
                      "Tokuno",
                      "Trammel"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AtlasStatus": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Admin view of atlas state: where the tree is, whether it is readable, whether it has drifted from what is loaded, and any refresh staged for review."
          },
          "properties": {
            "type": "object",
            "properties": {
              "configured": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "path": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "/srv/servuo"
                  }
                }
              },
              "treeReadable": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "drift": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "True when the tree's source hashes differ from the loaded atlas. NULL when the tree could not be read."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "facets": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  }
                }
              },
              "importedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "counts": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "additionalProperties": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "pending": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "A refresh that was parsed but NOT applied because it would remove a facet. `status` is pending or rejected."
                  },
                  "additionalProperties": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "AtlasRefreshResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Outcome of a refresh. Reported rather than thrown, so an unreadable tree is an answer and not a 500."
          },
          "properties": {
            "type": "object",
            "properties": {
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "skipped",
                      "unavailable",
                      "unchanged",
                      "imported",
                      "needsReview",
                      "failed",
                      "rejected",
                      "none"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "type": "string",
                    "example": "imported"
                  }
                }
              },
              "reason": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "path": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "counts": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "object"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "additionalProperties": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "addedFacets": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  }
                }
              },
              "removedFacets": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ClilocStatus": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Admin view of cliloc state: where the converted file is, whether it is readable, how many entries are loaded, and whether the file has drifted from them. `configured: false` is a supported state — item names then render as ids."
          },
          "properties": {
            "type": "object",
            "properties": {
              "configured": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "path": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "/srv/uo-client"
                  }
                }
              },
              "file": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "The file actually resolved, when the path is a directory."
                  },
                  "example": {
                    "type": "string",
                    "example": "/srv/uo-client/clilocs.tsv"
                  }
                }
              },
              "fileReadable": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "problem": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Why the file cannot be used, when it cannot. Set (with code COMPRESSED) for a readable-but-unconverted client file."
                  },
                  "example": {}
                }
              },
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Machine-readable cause of `problem`."
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "NO_PATH",
                      "NOT_FOUND",
                      "NO_FILE",
                      "UNREADABLE",
                      "COMPRESSED"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "drift": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "True when any source hash differs from the loaded table. NULL when the sources could not be read or are not usable."
                  },
                  "example": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "count": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "description": {
                    "type": "string",
                    "example": "Entries currently loaded."
                  },
                  "example": {
                    "type": "number",
                    "example": 67496
                  }
                }
              },
              "sources": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "Every source found now, root-relative, base first then overlays in merge order."
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "clilocs.plain",
                      "custom/uomysticmoon.tsv"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "loadedSources": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "What each source contributed at the last import."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "example": {
                                "type": "string",
                                "example": "custom/uomysticmoon.tsv"
                              }
                            }
                          },
                          "kind": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "enum": {
                                "type": "array",
                                "example": [
                                  "base",
                                  "custom"
                                ],
                                "items": {
                                  "type": "string"
                                }
                              },
                              "example": {
                                "type": "string",
                                "example": "custom"
                              }
                            }
                          },
                          "entries": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "example": {
                                "type": "number",
                                "example": 37
                              }
                            }
                          },
                          "added": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "description": {
                                "type": "string",
                                "example": "Ids this source introduced."
                              },
                              "example": {
                                "type": "number",
                                "example": 25
                              }
                            }
                          },
                          "overrode": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              },
                              "description": {
                                "type": "string",
                                "example": "Ids it replaced from an earlier source."
                              },
                              "example": {
                                "type": "number",
                                "example": 12
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "missingSources": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "Sources loaded previously and now absent. An import refuses these without `approve`."
                  },
                  "example": {
                    "type": "array",
                    "example": [],
                    "items": {}
                  }
                }
              },
              "importedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "sourceBytes": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 4973525
                  }
                }
              }
            }
          }
        }
      },
      "ClilocRefreshResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "Outcome of a cliloc refresh. Reported rather than thrown, so a missing or compressed file is an answer and not a 500."
          },
          "properties": {
            "type": "object",
            "properties": {
              "status": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "skipped",
                      "unavailable",
                      "unchanged",
                      "imported",
                      "needsReview",
                      "failed"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "`needsReview` means a previously-loaded source has vanished and nothing was applied; re-run with `approve` to accept it."
                  },
                  "example": {
                    "type": "string",
                    "example": "imported"
                  }
                }
              },
              "reason": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Machine-readable cause. `COMPRESSED` means the client's own Cliloc.enu was supplied instead of a converted one."
                  },
                  "enum": {
                    "type": "array",
                    "example": [
                      "NO_PATH",
                      "NOT_FOUND",
                      "NO_FILE",
                      "UNREADABLE",
                      "COMPRESSED",
                      "TRUNCATED",
                      "EMPTY",
                      "NOT_BUFFER"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "path": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "file": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "count": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Entries stored (blank strings are dropped)."
                  },
                  "example": {
                    "type": "number",
                    "example": 67496
                  }
                }
              },
              "parsed": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Entries read across every source before blanks were dropped."
                  },
                  "example": {
                    "type": "number",
                    "example": 123527
                  }
                }
              },
              "blank": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "number",
                    "example": 55994
                  }
                }
              },
              "sources": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "description": {
                    "type": "string",
                    "example": "Per-source breakdown: what each file contributed and how much of it overrode an earlier source."
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "object"
                      },
                      "properties": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              }
                            }
                          },
                          "kind": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "string"
                              },
                              "enum": {
                                "type": "array",
                                "example": [
                                  "base",
                                  "custom"
                                ],
                                "items": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "entries": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              }
                            }
                          },
                          "added": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              }
                            }
                          },
                          "overrode": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "example": "integer"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "missingSources": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "On `needsReview`: the sources that vanished. Nothing was applied."
                  }
                }
              },
              "acceptedMissing": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      }
                    }
                  },
                  "description": {
                    "type": "string",
                    "example": "On `imported` with `approve`: the vanished sources the admin accepted."
                  }
                }
              }
            }
          }
        }
      },
      "ShardLinkRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "code"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "code": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "description": {
                    "type": "string",
                    "example": "The one-time code shown by [link in game."
                  },
                  "example": {
                    "type": "string",
                    "example": "AB12CD"
                  }
                }
              }
            }
          }
        }
      },
      "ShardLinkResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "properties": {
            "type": "object",
            "properties": {
              "linked": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              },
              "account": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "whitlocktech"
                  }
                }
              }
            }
          }
        }
      },
      "ShardLink": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "description": {
            "type": "string",
            "example": "A linked in-game account (GET /player/shard/accounts)."
          },
          "properties": {
            "type": "object",
            "properties": {
              "account": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "example": {
                    "type": "string",
                    "example": "whitlocktech"
                  }
                }
              },
              "userId": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "example": {
                    "type": "number",
                    "example": 42
                  }
                }
              },
              "charName": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "nullable": {
                    "type": "boolean",
                    "example": true
                  },
                  "example": {
                    "type": "string",
                    "example": "Darrow"
                  }
                }
              },
              "linkedAt": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "format": {
                    "type": "string",
                    "example": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "TownCrierRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "object"
          },
          "required": {
            "type": "array",
            "example": [
              "id",
              "lines"
            ],
            "items": {
              "type": "string"
            }
          },
          "properties": {
            "type": "object",
            "properties": {
              "id": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "string"
                  },
                  "maxLength": {
                    "type": "number",
                    "example": 64
                  },
                  "description": {
                    "type": "string",
                    "example": "Re-posting the same id replaces the prior entry."
                  },
                  "example": {
                    "type": "string",
                    "example": "news-42"
                  }
                }
              },
              "lines": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "array"
                  },
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "example": "string"
                      },
                      "maxLength": {
                        "type": "number",
                        "example": 200
                      }
                    }
                  },
                  "example": {
                    "type": "array",
                    "example": [
                      "Hear ye!",
                      "Market tax is now 5%."
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "durationSec": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "integer"
                  },
                  "minimum": {
                    "type": "number",
                    "example": 1
                  },
                  "maximum": {
                    "type": "number",
                    "example": 86400
                  },
                  "example": {
                    "type": "number",
                    "example": 3600
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
