Files
website/server/swagger/swagger-output.json
Claude 74d2ead958 Let staff link their own characters + share the game-accounts UI
- Backend: /admin/shard/{link,accounts,roster/:account,vendors/:account} —
  staff self-service, reusing the player/shard controller (it keys off
  req.user.id, so the same handlers serve any logged-in role). Swagger under
  Admin · Account; spec regenerated.
- components/GameAccounts.jsx: the link-prompt + character-roster UI extracted
  into one reusable component parametrized by an api scope and a charTo(serial)
  route builder.
- PlayerCharacters now renders it (player scope → /player/char/:serial).
- Admin: "My Characters" nav item + /admin/characters (AdminCharacters) and
  /admin/characters/:serial (AdminCharacter, in-shell sheet), using the admin
  self-service scope. api.admin.shard.* added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qPmpmVH1xGCiZoz9m9vW3
2026-07-11 03:05:43 -05:00

11069 lines
288 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"openapi": "3.0.0",
"info": {
"title": "UOMysticmoon API",
"version": "1.0.0",
"description": "REST API for the UOMysticmoon website, wiki and admin panel — a private Ultima Online shard.\n\n### Authentication\n- **Web / admin panel** uses an httpOnly session cookie (`uomm_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/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": "Players online now (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/char/{serial}": {
"get": {
"tags": [
"Public · Shard"
],
"summary": "Live character sheet by serial (cached; degrades on shard restart)",
"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": "Invalid serial",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Character not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Internal Server Error"
},
"502": {
"description": "Bad Gateway"
},
"503": {
"description": "Shard restarting — retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/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 callers 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 a linked account (self)",
"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 a linked account (self)",
"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/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/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": []
}
]
}
},
"/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 players 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 players 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 callers 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/accounts": {
"get": {
"tags": [
"Player · Shard"
],
"summary": "List the callers 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": []
}
]
}
}
},
"components": {
"securitySchemes": {
"cookieAuth": {
"type": "apiKey",
"in": "cookie",
"name": "uomm_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/UOMysticmoon: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 player online now (redacted for the public list)."
},
"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"
}
}
}
}
}
}
},
"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
}
}
}
}
}
}
}
}
}
}