{
  "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": "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": "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/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/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/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/login/totp": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Complete login with a TOTP code",
        "description": "Second step for 2FA accounts. Exchange the challenge from /login plus the current authenticator code for a session cookie.",
        "responses": {
          "200": {
            "description": "Session issued",
            "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/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/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/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.",
        "responses": {
          "200": {
            "description": "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 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/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/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/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/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/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}/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/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. Rate limited and behind bot/backoff guards.",
        "responses": {
          "200": {
            "description": "Session issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/SafeUser"
                    },
                    "returnTo": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Invalid code or expired challenge",
            "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": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/settings": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Public site settings",
        "description": "Whitelisted, non-sensitive settings the client needs to render the site.",
        "responses": {
          "200": {
            "description": "Key/value settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/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.",
        "responses": {
          "200": {
            "description": "Site status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicStatus"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/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/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/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/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/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"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/feed": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Recent notable shard events (from the ingested log)",
        "description": "",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter to a single event kind, e.g. vendor.sale."
          },
          {
            "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"
          },
          "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"
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/online": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Staff online now (linked staff accounts; name + serial + map only)",
        "description": "",
        "responses": {
          "200": {
            "description": "Online players",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardOnlinePlayer"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/idoc": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Houses currently in danger (IDOC)",
        "description": "",
        "responses": {
          "200": {
            "description": "IDOC houses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShardHouse"
                  }
                }
              }
            }
          },
          "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
                  }
                }
              }
            }
          },
          "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
                  }
                }
              }
            }
          },
          "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
                  }
                }
              }
            }
          },
          "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"
          },
          "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
                }
              }
            }
          },
          "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"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/v1/public/shard/stream": {
      "get": {
        "tags": [
          "Public · Shard"
        ],
        "summary": "Live shard event stream (Server-Sent Events, public/safe kinds)",
        "description": "text/event-stream of curated live events. Sensitive kinds (staff audit, cheat detection, login attempts, IPs) are NOT sent on this channel.",
        "responses": {
          "200": {
            "description": "An SSE stream (Content-Type: text/event-stream)."
          }
        }
      }
    },
    "/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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}/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/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/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/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/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/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/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/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/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}/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/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/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": {
      "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/{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/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}/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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}/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/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}/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/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/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/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/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}/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/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/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/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/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/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": "Player role required, or account not active",
            "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": "Player role required, or account not active",
            "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/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": "Player role required, or account not active",
            "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/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/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/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/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/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/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/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/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/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/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/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": []
          }
        ]
      }
    }
  },
  "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",
              "code"
            ],
            "items": {
              "type": "string"
            }
          },
          "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"
                  },
                  "example": {
                    "type": "string",
                    "example": "123456"
                  }
                }
              }
            }
          }
        }
      },
      "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"
                  }
                }
              }
            }
          }
        }
      },
      "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"
              }
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      },
      "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."
          },
          "properties": {
            "type": "object",
            "properties": {
              "totp_enabled": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "boolean"
                  },
                  "example": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        }
      },
      "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": ""
                  }
                }
              }
            }
          }
        }
      },
      "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"
                  }
                }
              }
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}