{
  "openapi": "3.1.0",
  "info": {
    "title": "GAMPEON Platform API",
    "version": "0.1.0",
    "description": "The GAMPEON platform API: accounts, profiles, studios, projects, the server browser, the marketplace, the GAMPEONS wallet, the social layer (follows, friends, posts), jobs and communities with voice channels.\n\n**Status: Preview.** The API is in a closed test phase and can change without notice.\n\n## Conventions\n- Base URL: `https://api.gampeon.com`. All routes start with `/v1`.\n- Requests with a body send `Content-Type: application/json` (file uploads: the file type). Bodies are limited to 64 KB.\n- Times are Unix timestamps in **milliseconds** (UTC).\n- Errors have the shape `{ \"error\": \"code\", \"message\": \"optional detail\" }`. Codes are stable, messages are not.\n- Rate limits answer `429` with `Retry-After`.\n\n## Authentication\nLog in with `POST /v1/sessions` and send the token as `Authorization: Bearer <token>`. Sessions last 30 days. Game servers authenticate heartbeats with `x-server-key`.\n\n**Access tokens** (`gpat_…`, created in the portal or with `POST /v1/me/tokens`) are sent the same way, but only work on routes that list a scope in `x-gampeon-token-scope`, and only if the token has that scope (`any`: every valid token). Every other route, public ones included, answers `403 token_not_allowed` when called with an access token; a missing scope `403 insufficient_scope`. Tokens never see email addresses and can never moderate, administer, manage tokens, buy, pay, upload versions or delete. Limits per token: 120 requests per minute, 2000 per hour.\n\n## Errors on every route\n`400 invalid_json`, `400 body_must_be_object`, `400 invalid_field` (with a message naming the field), `404 not_found` for unknown paths, `405 method_not_allowed`, `413 body_too_large`, `415 unsupported_media_type`, `500 internal_error`.",
    "contact": {
      "name": "GAMPEON",
      "url": "https://create.gampeon.com/docs/",
      "email": "hello@gampeon.com"
    }
  },
  "servers": [
    {
      "url": "https://api.gampeon.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Accounts",
      "description": "Sign up, log in (with a password, Steam or Discord), connected accounts, email confirmation and password reset."
    },
    {
      "name": "Profiles",
      "description": "Public profiles and your own profile."
    },
    {
      "name": "Studios",
      "description": "Teams that own projects together. Membership only by invitation."
    },
    {
      "name": "Projects",
      "description": "Public pages of games: development stage, gallery, links, favorites, linked servers and marketplace items. New projects are drafts until the team publishes them."
    },
    {
      "name": "Servers",
      "description": "Server browser and heartbeats from game servers."
    },
    {
      "name": "Marketplace",
      "description": "Scripts, vehicles, maps and other content. Publishing requires the rights statement."
    },
    {
      "name": "Reports and moderation",
      "description": "Notice and action under the Digital Services Act (Art. 16, 17, 20)."
    },
    {
      "name": "Achievements",
      "description": "Achievements based on real activity."
    },
    {
      "name": "Access tokens",
      "description": "Personal access tokens (`gpat_…`) for AI assistants, MCP clients and tools, with scopes, expiry and an activity log."
    },
    {
      "name": "GAMPEONS",
      "description": "The platform currency: wallet, item purchases and creator payouts. Sandbox only: payments are simulated and no real money moves."
    },
    {
      "name": "Social",
      "description": "Follows, friends, blocks, privacy settings, posts and the feed."
    },
    {
      "name": "Communities",
      "description": "Optional communities of studios and projects in the launcher: channels in categories, roles with permissions, per-channel overrides, invites, kicks, bans, timeouts and an audit log. Reports on messages go to GAMPEON.\n\nPermissions are bitmasks. A member has the permissions of `everyone` plus all their roles (`administrator` means all). In a channel, the overrides of its category apply first, then its own: first `everyone`, then all of the member's roles together, denies before allows.\n\n| Name | Value | Meaning |\n|---|---|---|\n| `viewChannels` | 1 | See channels and read their messages. |\n| `sendMessages` | 2 | Write in text channels. |\n| `manageMessages` | 4 | Remove messages from others and ignore slow mode. |\n| `manageChannels` | 8 | Create, edit, reorder and delete channels and their permissions. |\n| `manageRoles` | 16 | Create and edit roles below their own highest role and assign them. |\n| `manageCommunity` | 32 | Change name, description, rules, who can join and the minimum age. |\n| `createInvites` | 64 | Create invite links. |\n| `kickMembers` | 128 | Remove members below their own highest role. They can join again. |\n| `banMembers` | 256 | Ban members below their own highest role and remove their recent messages. |\n| `timeoutMembers` | 512 | Mute members below their own highest role for a while. |\n| `viewAuditLog` | 1024 | See who changed what in this community. |\n| `administrator` | 2048 | Every permission in every channel. Give this only to people you trust. |\n| `connect` | 4096 | Join voice channels and listen. Voice is for people aged 16 and older. |\n| `speak` | 8192 | Talk in voice channels. |\n| `stream` | 16384 | Share a screen or a window with sound in voice channels. |\n| `muteMembers` | 32768 | Mute members below their own highest role in all voice channels. |\n| `moveMembers` | 65536 | Remove members below their own highest role from voice channels and join full ones. |"
    },
    {
      "name": "Voice",
      "description": "Voice channels in communities: talk, share a screen or a window with sound, watch others. Sound and video go directly between the launcher and the GAMPEON voice server (LiveKit); GAMPEON does not record them. The API decides who may join and what they may send, and keeps only who was in which channel when (30 days) for reports.\n\nVoice is for people aged 16 and older during the closed test. Permissions: `connect` (join and listen), `speak`, `stream` (share a screen with sound), `muteMembers` and `moveMembers` (disconnect, join full channels)."
    },
    {
      "name": "Game packages",
      "description": "Private game packages of a project (cooked Unreal content: maps, models, images for web interfaces). Only players of servers linked to the project get them, through the launcher when they press Play. Never in the marketplace and never downloadable in the portal."
    },
    {
      "name": "Jobs",
      "description": "Find people for your projects: job posts, applications and a conversation per application. No money or GAMPEONS move through GAMPEON; compensation is only stated."
    },
    {
      "name": "Meta",
      "description": "Health check, platform status and this description."
    }
  ],
  "paths": {
    "/healthz": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Meta"
        ],
        "summary": "Health check",
        "description": "Returns `{ \"ok\": true }` while the API is running. Used by monitoring.",
        "security": [],
        "responses": {
          "200": {
            "description": "API is up",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "const": true
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "tags": [
          "Meta"
        ],
        "summary": "This API description",
        "description": "The OpenAPI 3.1 description of this API. The developer docs and the GAMPEON MCP server are generated from it.",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/signup": {
      "get": {
        "operationId": "getSignupMode",
        "tags": [
          "Accounts"
        ],
        "summary": "Sign-up mode",
        "description": "Tells clients whether anyone can sign up (`open`) or only invited email addresses (`invite`, closed test phase), and whether email verification and password reset by email are active.",
        "security": [],
        "responses": {
          "200": {
            "description": "Sign-up settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string",
                      "enum": [
                        "open",
                        "invite"
                      ]
                    },
                    "emailVerification": {
                      "type": "boolean"
                    },
                    "passwordReset": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "mode",
                    "emailVerification",
                    "passwordReset"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/accounts": {
      "post": {
        "operationId": "createAccount",
        "tags": [
          "Accounts"
        ],
        "summary": "Create an account",
        "description": "Creates an account. Without a wished `handle`, one is derived from the display name. When email is active, the account must confirm its address before the first login.\n\nThe minimum age is 13. Under 16, stricter privacy defaults apply (see `GET /v1/me/social-settings`). The birth month is only visible to the account itself.\n\n`acceptTerms` must be the current version from `GET /v1/legal/terms`: the person has to agree to the Terms of Use and the Community Guidelines (links in that answer). The Privacy Policy is information, not consent.\n\nRate limit: 5 per hour per IP.",
        "security": [],
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "email": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "handle": {
                      "type": "string"
                    },
                    "verificationRequired": {
                      "type": "boolean",
                      "description": "An email with a confirmation link was sent"
                    }
                  },
                  "required": [
                    "id",
                    "email",
                    "displayName",
                    "handle",
                    "verificationRequired"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `signup_closed`, `too_young`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `email_taken`, `handle_taken`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `terms_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 5 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "5 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "displayName": {
                    "type": "string",
                    "description": "3 to 32 letters, digits, spaces, `_` or `-`",
                    "minLength": 3,
                    "maxLength": 32
                  },
                  "handle": {
                    "type": "string",
                    "description": "Optional wished handle",
                    "pattern": "^$|^[a-z0-9_]{3,24}$"
                  },
                  "password": {
                    "type": "string",
                    "description": "Password, 10 to 200 characters",
                    "minLength": 10,
                    "maxLength": 200
                  },
                  "birthMonth": {
                    "type": "string",
                    "description": "Birth month as `YYYY-MM`. Minimum age 13. Self-declared, only visible to the account itself.",
                    "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
                  },
                  "acceptTerms": {
                    "type": "string",
                    "description": "Current version from `GET /v1/legal/terms`. Sending it means the person accepts the Terms of Use and the Community Guidelines in that version.",
                    "maxLength": 40
                  }
                },
                "required": [
                  "email",
                  "displayName",
                  "password",
                  "birthMonth",
                  "acceptTerms"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/email/verify": {
      "post": {
        "operationId": "verifyEmail",
        "tags": [
          "Accounts"
        ],
        "summary": "Confirm an email address",
        "description": "Rate limit: 30 per hour per IP.",
        "security": [],
        "responses": {
          "200": {
            "description": "Address confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verified": {
                      "const": true
                    }
                  },
                  "required": [
                    "verified"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "30 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Token from the link in the email (the part after the last `/`)",
                    "minLength": 20,
                    "maxLength": 100
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/email/verify/resend": {
      "post": {
        "operationId": "resendVerification",
        "tags": [
          "Accounts"
        ],
        "summary": "Send a new confirmation link",
        "description": "Always answers `202`, whether or not an account exists for the address. At most 3 emails per address and hour.\n\nRate limit: 10 per hour per IP.",
        "security": [],
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "const": true
                    }
                  },
                  "required": [
                    "accepted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/password/forgot": {
      "post": {
        "operationId": "forgotPassword",
        "tags": [
          "Accounts"
        ],
        "summary": "Request a password reset link",
        "description": "Always answers `202`, whether or not an account exists for the address. The link is valid for one hour.\n\nRate limit: 10 per hour per IP.",
        "security": [],
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "const": true
                    }
                  },
                  "required": [
                    "accepted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/password/reset": {
      "post": {
        "operationId": "resetPassword",
        "tags": [
          "Accounts"
        ],
        "summary": "Set a new password",
        "description": "Sets a new password with the token from the reset email. Ends all sessions of the account.\n\nRate limit: 30 per hour per IP.",
        "security": [],
        "responses": {
          "200": {
            "description": "Password changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reset": {
                      "const": true
                    }
                  },
                  "required": [
                    "reset"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "30 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Token from the link in the email (the part after the last `/`)",
                    "minLength": 20,
                    "maxLength": 100
                  },
                  "password": {
                    "type": "string",
                    "description": "Password, 10 to 200 characters",
                    "minLength": 10,
                    "maxLength": 200
                  }
                },
                "required": [
                  "token",
                  "password"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/sessions": {
      "post": {
        "operationId": "login",
        "tags": [
          "Accounts"
        ],
        "summary": "Log in",
        "description": "Returns a bearer token for the `Authorization` header. Sessions last 30 days.\n\nRate limit: 5 failed attempts per email in 15 minutes, 30 attempts per IP in 15 minutes.",
        "security": [],
        "responses": {
          "201": {
            "description": "Logged in",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Bearer token. Shown once, store it securely."
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "token",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `invalid_credentials`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `email_unverified`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 5 failed attempts per email in 15 minutes, 30 attempts per IP in 15 minutes. Error codes: `rate_limited`, `too_many_attempts`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "5 failed attempts per email in 15 minutes, 30 attempts per IP in 15 minutes",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "password"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/sessions/current": {
      "delete": {
        "operationId": "logout",
        "tags": [
          "Accounts"
        ],
        "summary": "Log out",
        "description": "Ends the session of the token in the request.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Logged out"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/legal/terms": {
      "get": {
        "operationId": "getTerms",
        "tags": [
          "Accounts"
        ],
        "summary": "Current terms version",
        "description": "The version of the Terms of Use and Community Guidelines that is in force, the documents it covers, and links to the other legal texts on gampeon.com/legal/ (Privacy Policy, Legal Notice, notice-and-action policy, monetization terms, short version for teens). English is the main version; `urlDe` is the German version.",
        "security": [],
        "responses": {
          "200": {
            "description": "Terms version and links",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "documents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "terms",
                              "community-guidelines"
                            ]
                          },
                          "title": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "urlDe": {
                            "type": "string",
                            "format": "uri"
                          }
                        },
                        "required": [
                          "id",
                          "title",
                          "url",
                          "urlDe"
                        ]
                      }
                    },
                    "links": {
                      "type": "object",
                      "properties": {
                        "privacy": {
                          "type": "string"
                        },
                        "notice": {
                          "type": "string"
                        },
                        "copyright": {
                          "type": "string"
                        },
                        "monetization": {
                          "type": "string"
                        },
                        "teens": {
                          "type": "string"
                        },
                        "all": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "privacy",
                        "notice",
                        "copyright",
                        "monetization",
                        "teens",
                        "all"
                      ]
                    }
                  },
                  "required": [
                    "version",
                    "documents",
                    "links"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/me/terms": {
      "post": {
        "operationId": "acceptTerms",
        "tags": [
          "Accounts"
        ],
        "summary": "Accept the current terms",
        "description": "For accounts that have not accepted the version in force yet (`terms.current` is `false` in `GET /v1/me`). Only with a portal session, not with an access token: an AI assistant cannot accept terms for a person.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "Last accepted version, `null` if none yet (accounts created before the terms)"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "acceptedAt": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "currentVersion": {
                      "type": "string",
                      "description": "Version in force"
                    },
                    "current": {
                      "type": "boolean",
                      "description": "`false`: show the terms and ask the person to accept them (`POST /v1/me/terms`)"
                    }
                  },
                  "required": [
                    "version",
                    "acceptedAt",
                    "currentVersion",
                    "current"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `terms_outdated`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "string",
                    "description": "Current version from `GET /v1/legal/terms`",
                    "maxLength": 40
                  }
                },
                "required": [
                  "version"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Accounts"
        ],
        "summary": "Your account",
        "description": "Your account with the 50 newest notifications.\n\nAccess tokens: scope `profile:read`.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "profile:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "profile:read"
      },
      "patch": {
        "operationId": "updateMe",
        "tags": [
          "Profiles"
        ],
        "summary": "Change your profile",
        "description": "Changes display name, handle, bio, country or profile links. Fields you leave out stay unchanged. Accounts created before the age check add their `birthMonth` once; after that it can only be changed through support.\n\n`links` replaces the whole list. Each entry takes the name on the platform or a link to the profile there; GAMPEON keeps only the name and builds the address itself, so links to other sites are rejected. Entries with an empty `handle` are dropped. Country and links appear on the public profile only from age 16.\n\nNot available with access tokens.\n\nRate limit: 120 edits per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "bio": {
                      "type": "string"
                    },
                    "birthMonth": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "country": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "Country code (ISO 3166-1 alpha-2, for example `DE`). `null` when not set.",
                          "pattern": "^[A-Z]{2}$"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "links": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProfileLink"
                      },
                      "description": "Profiles on other platforms, at most one per platform (up to 10)."
                    }
                  },
                  "required": [
                    "handle",
                    "displayName",
                    "bio",
                    "birthMonth",
                    "country",
                    "links"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_country`, `invalid_link`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `too_young`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `handle_taken`, `birth_month_set`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 edits per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 edits per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "displayName": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 32
                  },
                  "handle": {
                    "type": "string",
                    "pattern": "^[a-z0-9_]{3,24}$"
                  },
                  "bio": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "birthMonth": {
                    "type": "string",
                    "description": "Birth month as `YYYY-MM`. Minimum age 13. Self-declared, only visible to the account itself.",
                    "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
                  },
                  "country": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Country code (ISO 3166-1 alpha-2, for example `DE`). `null` when not set.",
                        "pattern": "^[A-Z]{2}$"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "links": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "platform": {
                          "type": "string",
                          "enum": [
                            "twitch",
                            "kick",
                            "youtube",
                            "tiktok",
                            "instagram",
                            "x",
                            "facebook",
                            "reddit",
                            "github",
                            "bluesky"
                          ]
                        },
                        "handle": {
                          "type": "string",
                          "description": "Name on the platform or a link to the profile there. Empty removes it.",
                          "maxLength": 300
                        }
                      },
                      "required": [
                        "platform",
                        "handle"
                      ]
                    },
                    "description": "Profiles on other platforms, at most one per platform",
                    "maxItems": 10
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/profiles/{handle}": {
      "get": {
        "operationId": "getProfile",
        "tags": [
          "Profiles"
        ],
        "summary": "Public profile",
        "description": "Display name, bio, public marketplace items, servers online, studios, projects and achievements of an account. Never contains the email address.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/achievements": {
      "get": {
        "operationId": "listAchievements",
        "tags": [
          "Achievements"
        ],
        "summary": "Achievement catalog",
        "description": "All achievements with category, tier, points and rarity. When logged in, `summary`, `unlockedAt` and `progress` are filled for your account.\n\nAccess tokens: scope `profile:read`.",
        "security": [
          {},
          {
            "session": []
          },
          {
            "accessToken": [
              "profile:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Catalog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "categories": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AchievementCategory"
                      }
                    },
                    "tierPoints": {
                      "type": "object",
                      "properties": {
                        "bronze": {
                          "type": "integer"
                        },
                        "silver": {
                          "type": "integer"
                        },
                        "gold": {
                          "type": "integer"
                        },
                        "platinum": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "bronze",
                        "silver",
                        "gold",
                        "platinum"
                      ]
                    },
                    "accounts": {
                      "type": "integer",
                      "description": "Number of accounts (basis for `percent`)"
                    },
                    "achievements": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Achievement"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "maxPoints": {
                      "type": "integer"
                    },
                    "summary": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/AchievementSummary"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "categories",
                    "tierPoints",
                    "accounts",
                    "achievements",
                    "total",
                    "maxPoints",
                    "summary"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-token-scope": "profile:read"
      }
    },
    "/v1/profiles/{handle}/achievements": {
      "get": {
        "operationId": "listProfileAchievements",
        "tags": [
          "Achievements"
        ],
        "summary": "Achievements of a profile",
        "description": "All achievements with status, date, progress and rarity for this account.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Achievements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AchievementCategory"
                      }
                    },
                    "tierPoints": {
                      "type": "object",
                      "properties": {
                        "bronze": {
                          "type": "integer"
                        },
                        "silver": {
                          "type": "integer"
                        },
                        "gold": {
                          "type": "integer"
                        },
                        "platinum": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "bronze",
                        "silver",
                        "gold",
                        "platinum"
                      ]
                    },
                    "accounts": {
                      "type": "integer",
                      "description": "Number of accounts (basis for `percent`)"
                    },
                    "achievements": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Achievement"
                      }
                    },
                    "summary": {
                      "$ref": "#/components/schemas/AchievementSummary"
                    }
                  },
                  "required": [
                    "handle",
                    "displayName",
                    "categories",
                    "tierPoints",
                    "accounts",
                    "achievements",
                    "summary"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/admin/achievements": {
      "post": {
        "operationId": "grantAchievement",
        "tags": [
          "Achievements"
        ],
        "summary": "Grant a team achievement",
        "description": "Admins grant achievements with `source: staff`, for example for a game jam or a confirmed bug report.\n\nRequires the role admin.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Granted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "achievement": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "handle",
                    "achievement"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_unlocked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string"
                  },
                  "achievement": {
                    "type": "string",
                    "description": "Achievement ID"
                  }
                },
                "required": [
                  "handle",
                  "achievement"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/achievements/{handle}/{id}": {
      "delete": {
        "operationId": "revokeAchievement",
        "tags": [
          "Achievements"
        ],
        "summary": "Revoke an achievement",
        "description": "For example after manipulation (fake heartbeats, bought downloads).\n\nRequires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Achievement ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin"
      }
    },
    "/v1/studios": {
      "post": {
        "operationId": "createStudio",
        "tags": [
          "Studios"
        ],
        "summary": "Create a studio",
        "description": "You become its owner. At most 5 studios per account.\n\nNot available with access tokens.\n\nRate limit: 5 per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Studio created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "slug": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "tagline": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "website": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "slug",
                    "name",
                    "tagline",
                    "description",
                    "website"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `slug_taken`, `studio_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 5 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "5 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string",
                    "description": "3 to 32 characters `a-z`, `0-9`, `-`",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 48
                  },
                  "tagline": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "description": "Line breaks allowed",
                    "maxLength": 4000
                  },
                  "website": {
                    "type": "string",
                    "description": "Website. Only `https://` URLs, empty string removes it.",
                    "maxLength": 200,
                    "pattern": "^$|^https://[^\\s<>\"]+$"
                  }
                },
                "required": [
                  "slug",
                  "name"
                ]
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listStudios",
        "tags": [
          "Studios"
        ],
        "summary": "List studios",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in slug, name and tagline",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Default: players",
            "schema": {
              "type": "string",
              "enum": [
                "players",
                "members",
                "new",
                "name"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100 (default 24)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of entries to skip",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Studios",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "studios": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Studio"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "studios",
                    "total"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/studios/{slug}": {
      "get": {
        "operationId": "getStudio",
        "tags": [
          "Studios"
        ],
        "summary": "Studio page",
        "description": "Members, projects and your role (`viewerRole`). Owners and admins also see open invites.\n\nAccess tokens: scope `projects:read`.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          },
          {
            "accessToken": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Studio",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Studio"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "description": {
                          "type": "string"
                        },
                        "website": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "members": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/StudioMember"
                          }
                        },
                        "memberCount": {
                          "type": "integer"
                        },
                        "projects": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Project"
                          }
                        },
                        "projectCount": {
                          "type": "integer"
                        },
                        "viewerRole": {
                          "oneOf": [
                            {
                              "type": "string",
                              "enum": [
                                "owner",
                                "admin",
                                "member"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "invites": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/StudioInvite"
                          },
                          "description": "Only for owners and admins"
                        },
                        "hidden": {
                          "$ref": "#/components/schemas/Hidden"
                        }
                      },
                      "required": [
                        "description",
                        "website",
                        "updatedAt",
                        "members",
                        "memberCount",
                        "projects",
                        "projectCount",
                        "viewerRole"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-token-scope": "projects:read"
      },
      "patch": {
        "operationId": "updateStudio",
        "tags": [
          "Studios"
        ],
        "summary": "Change a studio",
        "description": "Owners and admins. Fields you leave out stay unchanged.\n\nNot available with access tokens.\n\nRate limit: 120 edits per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "slug": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "tagline": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "website": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "slug",
                    "name",
                    "tagline",
                    "description",
                    "website"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 edits per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 edits per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 48
                  },
                  "tagline": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 4000
                  },
                  "website": {
                    "type": "string",
                    "description": "Website. Only `https://` URLs, empty string removes it.",
                    "maxLength": 200,
                    "pattern": "^$|^https://[^\\s<>\"]+$"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteStudio",
        "tags": [
          "Studios"
        ],
        "summary": "Delete a studio",
        "description": "Only the owner, and only while the studio has no projects.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `studio_has_projects`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/studios/{slug}/invites": {
      "post": {
        "operationId": "inviteToStudio",
        "tags": [
          "Studios"
        ],
        "summary": "Invite someone",
        "description": "Owners and admins invite by handle. Only the owner can invite admins. Invites are valid for 14 days; the invited person accepts them. At most 50 members.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Invite sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "handle": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "id",
                    "handle",
                    "displayName",
                    "role",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`, `user_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_member`, `invite_exists`, `invite_limit_reached`, `member_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string",
                    "description": "Handle, with or without `@`"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "member",
                      "admin"
                    ],
                    "description": "Default: member"
                  }
                },
                "required": [
                  "handle"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/studios/{slug}/invites/{id}": {
      "delete": {
        "operationId": "cancelStudioInvite",
        "tags": [
          "Studios"
        ],
        "summary": "Withdraw an invite",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Invite ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/studios/{slug}/members/{handle}": {
      "patch": {
        "operationId": "setStudioRole",
        "tags": [
          "Studios"
        ],
        "summary": "Change a member role",
        "description": "Only the owner. The owner role itself cannot change.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Role changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "handle",
                    "role"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `owner_role_fixed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "admin",
                      "member"
                    ]
                  }
                },
                "required": [
                  "role"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "removeStudioMember",
        "tags": [
          "Studios"
        ],
        "summary": "Leave or remove a member",
        "description": "Members can leave (not the owner). The owner removes anyone, admins remove members. Their own servers and marketplace items are unlinked from the studio projects.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `owner_cannot_leave`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/studios": {
      "get": {
        "operationId": "listMyStudios",
        "tags": [
          "Studios"
        ],
        "summary": "Your studios",
        "description": "Studios you belong to with your role, including hidden ones (`hidden: true`).\n\nAccess tokens: scope `profile:read`.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "profile:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Studios",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "studios": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StudioMembership"
                      }
                    }
                  },
                  "required": [
                    "studios"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "profile:read"
      }
    },
    "/v1/me/invites": {
      "get": {
        "operationId": "listMyInvites",
        "tags": [
          "Studios"
        ],
        "summary": "Your open invites",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Invites",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invites": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "expiresAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "studio": {
                            "type": "object",
                            "properties": {
                              "slug": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "slug",
                              "name"
                            ]
                          },
                          "invitedBy": {
                            "$ref": "#/components/schemas/UserRef"
                          }
                        },
                        "required": [
                          "id",
                          "role",
                          "createdAt",
                          "expiresAt",
                          "studio",
                          "invitedBy"
                        ]
                      }
                    }
                  },
                  "required": [
                    "invites"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/invites/{id}": {
      "post": {
        "operationId": "answerInvite",
        "tags": [
          "Studios"
        ],
        "summary": "Accept or decline an invite",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Invite ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Answered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "joined": {
                      "type": "boolean"
                    },
                    "studio": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "joined"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `member_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "accept": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "accept"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/projects": {
      "post": {
        "operationId": "createProject",
        "tags": [
          "Projects"
        ],
        "summary": "Create a project",
        "description": "A project is the public page of a game. With `studio`, the project belongs to that studio (owners and admins only). New projects are drafts that only the team sees, until they are published (`published: true` here or later with a change). At most 20 projects per account and 50 per studio.\n\nAccess tokens: scope `projects:write`.\n\nRate limit: 10 per hour per account.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "projects:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Project created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `studio_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `slug_taken`, `project_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `publish_requirements`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "projects:write",
        "x-gampeon-rate-limit": "10 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                  },
                  "title": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 64
                  },
                  "tagline": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "description": "Line breaks allowed",
                    "maxLength": 8000
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "roleplay",
                      "racing",
                      "shooter",
                      "survival",
                      "parkour",
                      "sandbox",
                      "minigames",
                      "simulation",
                      "other"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "concept",
                      "development",
                      "alpha",
                      "beta",
                      "released"
                    ],
                    "description": "Default: development. `playtest` from older clients counts as beta"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9\\-]{0,23}$"
                    },
                    "description": "Up to 10 lowercase tags (`a-z`, `0-9`, `-`, max. 24 characters each).",
                    "maxItems": 10
                  },
                  "languages": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "en",
                        "de",
                        "fr",
                        "es",
                        "it",
                        "pt",
                        "nl",
                        "pl",
                        "cs",
                        "sk",
                        "hu",
                        "ro",
                        "bg",
                        "el",
                        "tr",
                        "ru",
                        "uk",
                        "sv",
                        "da",
                        "no",
                        "fi",
                        "ar",
                        "he",
                        "hi",
                        "th",
                        "vi",
                        "id",
                        "ja",
                        "ko",
                        "zh"
                      ]
                    },
                    "description": "Up to 10 languages (ISO 639-1)",
                    "maxItems": 10
                  },
                  "links": {
                    "$ref": "#/components/schemas/ProjectLinks"
                  },
                  "website": {
                    "type": "string",
                    "description": "Website. Only `https://` URLs, empty string removes it.",
                    "maxLength": 200,
                    "pattern": "^$|^https://[^\\s<>\"]+$"
                  },
                  "studio": {
                    "type": "string",
                    "description": "Studio slug (optional)"
                  },
                  "published": {
                    "type": "boolean",
                    "description": "Publish right away. Needs a tagline and an about text with at least 100 characters. Default: draft"
                  }
                },
                "required": [
                  "slug",
                  "title",
                  "genre"
                ]
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Projects"
        ],
        "summary": "List projects",
        "description": "Published projects only. `facets` counts genres without the genre filter and statuses within the chosen genre.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in slug, title, tagline, studio and owner",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "genre",
            "in": "query",
            "required": false,
            "description": "Filter by genre",
            "schema": {
              "type": "string",
              "enum": [
                "roleplay",
                "racing",
                "shooter",
                "survival",
                "parkour",
                "sandbox",
                "minigames",
                "simulation",
                "other"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by status",
            "schema": {
              "type": "string",
              "enum": [
                "concept",
                "development",
                "alpha",
                "beta",
                "released"
              ]
            }
          },
          {
            "name": "studio",
            "in": "query",
            "required": false,
            "description": "Filter by studio slug",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Default: players",
            "schema": {
              "type": "string",
              "enum": [
                "players",
                "updated",
                "new",
                "name"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100 (default 24)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of entries to skip",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "genres": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "statuses": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "facets": {
                      "type": "object",
                      "properties": {
                        "genres": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "status": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        }
                      },
                      "required": [
                        "genres",
                        "status"
                      ]
                    }
                  },
                  "required": [
                    "projects",
                    "total",
                    "genres",
                    "statuses",
                    "facets"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/projects/{slug}": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Projects"
        ],
        "summary": "Project page",
        "description": "Gallery, links, linked servers, marketplace items and team. For studio projects, `owner` also has `members` and `projects` (published). `viewer` tells whether you may edit the project or link your servers and items to it, and whether it is one of your favorites. The team also gets the `checklist` for publishing. Drafts and hidden projects: only the team and moderation.\n\nAccess tokens: scope `projects:read`.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          },
          {
            "accessToken": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Project",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Project"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "description": {
                          "type": "string",
                          "description": "Markdown: headings, lists, quotes, bold, italic, links"
                        },
                        "website": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "links": {
                          "$ref": "#/components/schemas/ProjectLinks"
                        },
                        "gallery": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/GalleryItem"
                          },
                          "description": "Screenshots and videos in order"
                        },
                        "peakPlayers": {
                          "type": "integer"
                        },
                        "servers": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Server"
                          }
                        },
                        "resources": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ResourceListing"
                          }
                        },
                        "team": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "handle": {
                                "type": "string"
                              },
                              "displayName": {
                                "type": "string"
                              },
                              "avatar": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "description": "Image path (`/v1/media/…`)"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "role": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "handle",
                              "displayName",
                              "avatar",
                              "role"
                            ]
                          }
                        },
                        "viewer": {
                          "type": "object",
                          "properties": {
                            "canEdit": {
                              "type": "boolean"
                            },
                            "canLink": {
                              "type": "boolean"
                            },
                            "favorited": {
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "canEdit",
                            "canLink",
                            "favorited"
                          ]
                        },
                        "community": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "id": {
                                  "type": "integer"
                                },
                                "name": {
                                  "type": "string"
                                },
                                "scope": {
                                  "type": "string",
                                  "enum": [
                                    "project",
                                    "studio"
                                  ],
                                  "description": "Community of the project itself, or of its studio"
                                },
                                "joinMode": {
                                  "type": "string",
                                  "enum": [
                                    "open",
                                    "invite"
                                  ]
                                },
                                "member": {
                                  "type": "boolean",
                                  "description": "You are a member"
                                }
                              },
                              "required": [
                                "id",
                                "name",
                                "scope",
                                "joinMode",
                                "member"
                              ],
                              "description": "Community for the button on the game page: the project's own, otherwise the studio's. Only when you can see it (open, member or team)."
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "checklist": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ChecklistItem"
                          },
                          "description": "Only for the team"
                        },
                        "hidden": {
                          "$ref": "#/components/schemas/Hidden"
                        }
                      },
                      "required": [
                        "description",
                        "website",
                        "links",
                        "gallery",
                        "peakPlayers",
                        "servers",
                        "resources",
                        "team",
                        "viewer",
                        "community"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-token-scope": "projects:read"
      },
      "patch": {
        "operationId": "updateProject",
        "tags": [
          "Projects"
        ],
        "summary": "Change a project",
        "description": "Owner of the project, or owners and admins of its studio. Fields you leave out stay unchanged; `links` only changes the links you send. `published: true` publishes a draft (needs a tagline and an about text with at least 100 characters), `false` turns it back into a draft.\n\nAccess tokens: scope `projects:write`.\n\nRate limit: 120 edits per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "projects:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Updated project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `publish_requirements`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 edits per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "projects:write",
        "x-gampeon-rate-limit": "120 edits per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 64
                  },
                  "tagline": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "description": "Markdown",
                    "maxLength": 8000
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "roleplay",
                      "racing",
                      "shooter",
                      "survival",
                      "parkour",
                      "sandbox",
                      "minigames",
                      "simulation",
                      "other"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "concept",
                      "development",
                      "alpha",
                      "beta",
                      "released"
                    ]
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9\\-]{0,23}$"
                    },
                    "description": "Up to 10 lowercase tags (`a-z`, `0-9`, `-`, max. 24 characters each).",
                    "maxItems": 10
                  },
                  "languages": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "en",
                        "de",
                        "fr",
                        "es",
                        "it",
                        "pt",
                        "nl",
                        "pl",
                        "cs",
                        "sk",
                        "hu",
                        "ro",
                        "bg",
                        "el",
                        "tr",
                        "ru",
                        "uk",
                        "sv",
                        "da",
                        "no",
                        "fi",
                        "ar",
                        "he",
                        "hi",
                        "th",
                        "vi",
                        "id",
                        "ja",
                        "ko",
                        "zh"
                      ]
                    },
                    "description": "Up to 10 languages (ISO 639-1)",
                    "maxItems": 10
                  },
                  "links": {
                    "$ref": "#/components/schemas/ProjectLinks"
                  },
                  "website": {
                    "type": "string",
                    "description": "Website. Only `https://` URLs, empty string removes it.",
                    "maxLength": 200,
                    "pattern": "^$|^https://[^\\s<>\"]+$"
                  },
                  "published": {
                    "type": "boolean"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteProject",
        "tags": [
          "Projects"
        ],
        "summary": "Delete a project",
        "description": "Removes the page. Linked servers and marketplace items stay, only the link is removed.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/projects": {
      "get": {
        "operationId": "listMyProjects",
        "tags": [
          "Projects"
        ],
        "summary": "Projects you work on",
        "description": "Own projects and projects of your studios, with `canEdit`.\n\nAccess tokens: scope `projects:read`.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/Project"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "canEdit": {
                                "type": "boolean"
                              },
                              "hidden": {
                                "type": "boolean"
                              }
                            },
                            "required": [
                              "canEdit"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "projects"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "projects:read"
      }
    },
    "/v1/me/favorites": {
      "get": {
        "operationId": "listMyFavorites",
        "tags": [
          "Projects"
        ],
        "summary": "Your favorite projects",
        "description": "Public projects you marked as favorite, most recent first (up to 200). The launcher shows them in the library.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    }
                  },
                  "required": [
                    "projects"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/servers": {
      "post": {
        "operationId": "createServer",
        "tags": [
          "Servers"
        ],
        "summary": "Register a server",
        "description": "Returns the server key **once**. The game server sends it as `x-server-key` with every heartbeat. At most 10 servers per account.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Server registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "serverKey": {
                      "type": "string",
                      "description": "Secret key. Shown once; store it on the game server only."
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "serverKey"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `server_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 64
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listServers",
        "tags": [
          "Servers"
        ],
        "summary": "Server browser",
        "description": "Servers that are online, sorted by player count (up to 500).",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in the server name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "Filter by tag",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Servers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "servers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Server"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "servers",
                    "total"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/servers/{id}/heartbeat": {
      "post": {
        "operationId": "serverHeartbeat",
        "tags": [
          "Servers"
        ],
        "summary": "Heartbeat",
        "description": "Send every 30 seconds. Servers without a heartbeat for 90 seconds disappear from the server browser. Without `address`, the IP of the request is used (set `sv_publicAddress` in `server.cfg` to override it).\n\nRate limit: 20 per minute per server and IP.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Server ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-server-key",
            "in": "header",
            "required": true,
            "description": "Key from registering the server",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "serverKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "nextHeartbeatInMs": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "ok",
                    "nextHeartbeatInMs"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `invalid_server_key`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per minute per server and IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "serverKey",
        "x-gampeon-rate-limit": "20 per minute per server and IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "players": {
                    "type": "integer",
                    "description": "Players online (0 to `maxPlayers`)",
                    "minimum": 0
                  },
                  "maxPlayers": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 2048
                  },
                  "port": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 65535
                  },
                  "address": {
                    "type": "string",
                    "description": "Host name or IP",
                    "maxLength": 253,
                    "pattern": "^[A-Za-z0-9.\\-:]*$"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Up to 10 tags, such as `roleplay` or `racing`",
                    "maxItems": 10
                  },
                  "language": {
                    "type": "string",
                    "description": "Language code such as `en` or `de-DE`",
                    "maxLength": 8
                  },
                  "packages": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "version": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "name",
                        "version"
                      ]
                    },
                    "description": "Game packages the server has loaded, up to 8. Players get them through the launcher when they join; they must be ready in the project the server is linked to.",
                    "maxItems": 8
                  }
                },
                "required": [
                  "players",
                  "maxPlayers",
                  "port"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/servers/{id}": {
      "get": {
        "operationId": "getServer",
        "tags": [
          "Servers"
        ],
        "summary": "Server details",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Server ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Server",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Server"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      },
      "patch": {
        "operationId": "updateServer",
        "tags": [
          "Servers"
        ],
        "summary": "Change a server",
        "description": "Only the owner. Fields you leave out stay unchanged.\n\nNot available with access tokens.\n\nRate limit: 120 edits per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Server ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "website": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "project": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/ProjectRef"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "description",
                    "website",
                    "project"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`, `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 edits per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 edits per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 64
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "website": {
                    "type": "string",
                    "description": "Website. Only `https://` URLs, empty string removes it.",
                    "maxLength": 200,
                    "pattern": "^$|^https://[^\\s<>\"]+$"
                  },
                  "project": {
                    "oneOf": [
                      {
                        "type": "string",
                        "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                      },
                      {
                        "type": "null"
                      },
                      {
                        "const": ""
                      }
                    ],
                    "description": "Slug of a project you work on (owner, studio member). `null` or an empty string removes the link."
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteServer",
        "tags": [
          "Servers"
        ],
        "summary": "Delete a server",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Server ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/servers": {
      "get": {
        "operationId": "listMyServers",
        "tags": [
          "Servers"
        ],
        "summary": "Your servers",
        "description": "All your servers, including offline ones. Never contains the server key.\n\nAccess tokens: scope `servers:read`.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "servers:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Servers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "servers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Server"
                      }
                    }
                  },
                  "required": [
                    "servers"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "servers:read"
      }
    },
    "/v1/legal/rights-statement": {
      "get": {
        "operationId": "getRightsStatement",
        "tags": [
          "Marketplace"
        ],
        "summary": "Rights statement",
        "description": "The exact text a creator must confirm before publishing a version. Send it back unchanged as `rightsStatement`.",
        "security": [],
        "responses": {
          "200": {
            "description": "Statement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "statement": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "statement"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/resources": {
      "get": {
        "operationId": "searchMarketplace",
        "tags": [
          "Marketplace"
        ],
        "summary": "Search the marketplace",
        "description": "Only items with at least one version that is not blocked. `facets.kinds` counts per kind without the kind filter; `facets.tags` lists the most used tags within the kind.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in name, title and description",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Filter by kind",
            "schema": {
              "type": "string",
              "enum": [
                "script",
                "vehicle",
                "map",
                "prop",
                "clothing",
                "gamemode",
                "tool"
              ]
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "Filter by tag",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "description": "Filter by owner handle",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Default: recently updated",
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "created",
                "downloads",
                "name"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100 (default 24)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of entries to skip",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ResourceListing"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "kinds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "facets": {
                      "type": "object",
                      "properties": {
                        "kinds": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "tags": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "tag": {
                                "type": "string"
                              },
                              "n": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "tag",
                              "n"
                            ]
                          }
                        }
                      },
                      "required": [
                        "kinds",
                        "tags"
                      ]
                    }
                  },
                  "required": [
                    "resources",
                    "total",
                    "kinds",
                    "facets"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      },
      "post": {
        "operationId": "createResource",
        "tags": [
          "Marketplace"
        ],
        "summary": "Create a marketplace item",
        "description": "Creates the entry. It becomes visible once a version is published. New items are free; set a price in GAMPEONS later with `PATCH`.\n\nAccess tokens: scope `marketplace:write`.\n\nRate limit: 20 per hour per account.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "marketplace:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "name",
                    "title",
                    "description",
                    "kind",
                    "tags"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `name_taken`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "marketplace:write",
        "x-gampeon-rate-limit": "20 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Unique name, `a-z`, `0-9`, `_`, `-`",
                    "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
                  },
                  "title": {
                    "type": "string",
                    "description": "Default: the name",
                    "maxLength": 80
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "script",
                      "vehicle",
                      "map",
                      "prop",
                      "clothing",
                      "gamemode",
                      "tool"
                    ],
                    "description": "Default: script"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9\\-]{0,23}$"
                    },
                    "description": "Up to 10 lowercase tags (`a-z`, `0-9`, `-`, max. 24 characters each).",
                    "maxItems": 10
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/resources/{name}": {
      "get": {
        "operationId": "getResource",
        "tags": [
          "Marketplace"
        ],
        "summary": "Marketplace item",
        "description": "With all versions that are not blocked. For paid items the version `url` is `null`; buyers get a download link from `…/download`. With a session, `owned` says whether you may download it.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Item",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceDetail"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      },
      "patch": {
        "operationId": "updateResource",
        "tags": [
          "Marketplace"
        ],
        "summary": "Change a marketplace item",
        "description": "Only the owner. Fields you leave out stay unchanged. A price above 0 requires the seller terms (`POST /v1/wallet/seller-terms`) and an active GAMPEONS mode.\n\nAccess tokens: scope `marketplace:write`, without `price`.\n\nRate limit: 120 edits per hour per account.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "marketplace:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "project": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/ProjectRef"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "price": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "name",
                    "title",
                    "description",
                    "kind",
                    "tags",
                    "project",
                    "price"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`, `forbidden`, `seller_terms_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`, `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 edits per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "marketplace:write",
        "x-gampeon-rate-limit": "120 edits per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "script",
                      "vehicle",
                      "map",
                      "prop",
                      "clothing",
                      "gamemode",
                      "tool"
                    ]
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9\\-]{0,23}$"
                    },
                    "description": "Up to 10 lowercase tags (`a-z`, `0-9`, `-`, max. 24 characters each).",
                    "maxItems": 10
                  },
                  "project": {
                    "oneOf": [
                      {
                        "type": "string",
                        "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                      },
                      {
                        "type": "null"
                      },
                      {
                        "const": ""
                      }
                    ],
                    "description": "Slug of a project you work on (owner, studio member). `null` or an empty string removes the link."
                  },
                  "price": {
                    "type": "integer",
                    "description": "Price in GAMPEONS. 0 = free, otherwise 10 to 1,000,000",
                    "minimum": 0,
                    "maximum": 1000000
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/resources/{name}/versions": {
      "post": {
        "operationId": "createVersion",
        "tags": [
          "Marketplace"
        ],
        "summary": "Announce a version",
        "description": "Registers a version with size and checksum. **Requires the explicit rights confirmation by the creator**: `rightsConfirmed: true` and the exact current text from `GET /v1/legal/rights-statement`. Both are stored. Then upload the file with `PUT …/file`.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Version registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "sizeBytes": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "name",
                    "version",
                    "sha256",
                    "sizeBytes"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `version_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `rights_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "string",
                    "description": "Semantic version, for example `1.0.0`",
                    "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.\\-]+)?$"
                  },
                  "sha256": {
                    "type": "string",
                    "description": "SHA-256 of the file (lowercase hex)",
                    "pattern": "^[a-f0-9]{64}$"
                  },
                  "sizeBytes": {
                    "type": "integer",
                    "description": "File size in bytes",
                    "minimum": 1
                  },
                  "rightsConfirmed": {
                    "const": true
                  },
                  "rightsStatement": {
                    "type": "string",
                    "description": "Exact text of the current rights statement"
                  }
                },
                "required": [
                  "version",
                  "sha256",
                  "sizeBytes",
                  "rightsConfirmed",
                  "rightsStatement"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/resources/{name}/versions/{version}/download": {
      "get": {
        "operationId": "getDownloadInfo",
        "tags": [
          "Marketplace"
        ],
        "summary": "Download information",
        "description": "For paid items only the owner, moderators and buyers get an answer. Their `url` carries a `grant` that is valid for 10 minutes.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Version",
            "schema": {
              "type": "string",
              "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.\\-]+)?$"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Checksum, size and file path",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sha256": {
                      "type": "string"
                    },
                    "sizeBytes": {
                      "type": "integer"
                    },
                    "url": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "Path relative to the API, `null` until uploaded"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "sha256",
                    "sizeBytes",
                    "url"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `purchase_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "451": {
            "description": "Error codes: `unavailable_for_legal_reasons`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/resources/{name}/versions/{version}/file": {
      "put": {
        "operationId": "uploadVersionFile",
        "tags": [
          "Marketplace"
        ],
        "summary": "Upload the file of a version",
        "description": "Only the owner, once per version. Size and SHA-256 must match the announced version. Default limits: 256 MB per file, 2 GB per account.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Version",
            "schema": {
              "type": "string",
              "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.\\-]+)?$"
            }
          },
          {
            "name": "x-file-name",
            "in": "header",
            "required": false,
            "description": "Optional file name for downloads (sanitised)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "sizeBytes": {
                      "type": "integer"
                    },
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "version",
                    "sha256",
                    "sizeBytes",
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `size_mismatch`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `file_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`, `quota_exceeded`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `checksum_mismatch`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "451": {
            "description": "Error codes: `unavailable_for_legal_reasons`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account",
        "requestBody": {
          "required": true,
          "description": "The file",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "downloadVersionFile",
        "tags": [
          "Marketplace"
        ],
        "summary": "Download a version",
        "description": "Counts at most one download per IP and version in 24 hours. Paid items need the `grant` from `…/download`.\n\nRate limit: 120 per hour per IP.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Version",
            "schema": {
              "type": "string",
              "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.\\-]+)?$"
            }
          },
          {
            "name": "grant",
            "in": "query",
            "required": false,
            "description": "Download grant for paid items, from `…/download`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The file as attachment",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `purchase_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`, `file_missing`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "451": {
            "description": "Error codes: `unavailable_for_legal_reasons`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "120 per hour per IP"
      }
    },
    "/v1/resources/{name}/image": {
      "put": {
        "operationId": "uploadResourceImage",
        "tags": [
          "Marketplace"
        ],
        "summary": "Upload a preview image",
        "description": "PNG, JPEG or WebP up to 2 MB. The type is checked against the first bytes of the file. No SVG.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "image": {
                      "type": "string",
                      "description": "Path relative to the API"
                    }
                  },
                  "required": [
                    "image"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      }
    },
    "/v1/images/{file}": {
      "get": {
        "operationId": "getImage",
        "tags": [
          "Marketplace"
        ],
        "summary": "Preview image",
        "description": "Only delivered while the item has a version that is not blocked.",
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "description": "`<sha256>.<png|jpg|webp>`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The image",
            "content": {
              "image/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/me/resources": {
      "get": {
        "operationId": "listMyResources",
        "tags": [
          "Marketplace"
        ],
        "summary": "Your marketplace items",
        "description": "Including blocked versions, with storage used and quota.\n\nAccess tokens: scope `marketplace:read`.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "marketplace:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MyResource"
                      }
                    },
                    "storage": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "usedBytes": {
                              "type": "integer"
                            },
                            "quotaBytes": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "usedBytes",
                            "quotaBytes"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "resources",
                    "storage"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "marketplace:read"
      }
    },
    "/v1/legal/report-categories": {
      "get": {
        "operationId": "getReportCategories",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report categories",
        "security": [],
        "responses": {
          "200": {
            "description": "Categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "copyright",
                          "trademark",
                          "illegal_content",
                          "malware",
                          "other"
                        ]
                      }
                    }
                  },
                  "required": [
                    "categories"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/reports": {
      "post": {
        "operationId": "createReport",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report content",
        "description": "Notice under Art. 16 DSA. Works without an account. The reporting person gets a confirmation and later the decision.\n\nRate limit: 10 per hour per IP.",
        "security": [],
        "responses": {
          "201": {
            "description": "Report received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "resource": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
                  },
                  "version": {
                    "type": "string",
                    "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.\\-]+)?$"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "copyright",
                      "trademark",
                      "illegal_content",
                      "malware",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "resource",
                  "version",
                  "category",
                  "explanation",
                  "reporterName",
                  "reporterEmail",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/reports": {
      "get": {
        "operationId": "listReports",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Reports",
        "description": "Requires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default: open",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "actioned",
                "rejected"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 200 reports, oldest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "category": {
                            "type": "string"
                          },
                          "explanation": {
                            "type": "string"
                          },
                          "reporterName": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "resource": {
                            "type": "string"
                          },
                          "version": {
                            "type": "string"
                          },
                          "blocked": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "id",
                          "category",
                          "explanation",
                          "reporterName",
                          "status",
                          "createdAt",
                          "resource",
                          "version",
                          "blocked"
                        ]
                      }
                    }
                  },
                  "required": [
                    "reports"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/reports/{id}/decision": {
      "post": {
        "operationId": "decideReport",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide on a report",
        "description": "`block` blocks the version and informs the creator with the statement of reasons (Art. 17 DSA). `reject` keeps it. The reporting person is informed either way.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Report ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "block",
                      "reject"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, 20 to 5000 characters. Shown to the people affected.",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/appeals": {
      "post": {
        "operationId": "createAppeal",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Appeal a decision",
        "description": "Complaint by the creator under Art. 20 DSA, one per decision.\n\nNot available with access tokens.\n\nRate limit: 10 per day per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Appeal received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appealId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "appealId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `appeal_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per day per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per day per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decisionId": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "decisionId",
                  "text"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/appeals": {
      "get": {
        "operationId": "listMyAppeals",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Your appeals",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Appeals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "decisionId": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "upheld",
                              "reversed"
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "outcomeReasons": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "decisionId",
                          "status",
                          "createdAt",
                          "decidedAt",
                          "outcomeReasons"
                        ]
                      }
                    }
                  },
                  "required": [
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/moderation/appeals": {
      "get": {
        "operationId": "listAppeals",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Appeals",
        "description": "`ownDecision` shows whether you made the original decision yourself; appeals should be reviewed by someone else where possible.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default: open",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "upheld",
                "reversed"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 200 appeals, oldest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "ownDecision": {
                            "type": "boolean"
                          },
                          "blocked": {
                            "type": "boolean"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/appeals/{id}/decision": {
      "post": {
        "operationId": "decideAppeal",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide on an appeal",
        "description": "`reverse` unblocks the version if no other block applies. The creator gets the statement of reasons and the notice on Art. 21 DSA.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Appeal ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appealId": {
                      "type": "string"
                    },
                    "outcome": {
                      "type": "string"
                    },
                    "unblocked": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "appealId",
                    "outcome",
                    "unblocked"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "uphold",
                      "reverse"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, 20 to 5000 characters. Shown to the people affected.",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "outcome",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/stats": {
      "get": {
        "operationId": "getModerationStats",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Numbers for the transparency report",
        "description": "Requires the role moderator or admin.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "decisions": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  },
                  "required": [
                    "reports",
                    "decisions",
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/pages/{type}/{slug}": {
      "post": {
        "operationId": "moderatePage",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Hide or restore a studio or project page",
        "description": "With a statement of reasons (Art. 17 DSA). Owners and admins are informed, every decision is logged.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug of the page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "type",
                    "slug",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_hidden`, `not_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "hide",
                      "restore"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, 20 to 5000 characters. Shown to the people affected.",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/pages": {
      "get": {
        "operationId": "listHiddenPages",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Hidden pages",
        "description": "Requires the role moderator or admin.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Hidden studios and projects and decision counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hidden": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "hiddenAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "reason": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "type",
                          "slug",
                          "title",
                          "hiddenAt",
                          "reason"
                        ]
                      }
                    },
                    "decisions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "type",
                          "action",
                          "n"
                        ]
                      }
                    }
                  },
                  "required": [
                    "hidden",
                    "decisions"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/wallet/packages": {
      "get": {
        "operationId": "listCoinPackages",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "GAMPEONS packages and rules",
        "description": "Packages with euro prices including VAT. `reference` is the basis for the euro value shown next to prices in GAMPEONS.",
        "security": [],
        "responses": {
          "200": {
            "description": "Packages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string",
                      "enum": [
                        "off",
                        "sandbox"
                      ],
                      "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                    },
                    "provider": {
                      "type": "string",
                      "enum": [
                        "sandbox",
                        "xsolla"
                      ],
                      "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                    },
                    "currency": {
                      "type": "string"
                    },
                    "packages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "coins": {
                            "type": "integer",
                            "description": "Amount in GAMPEONS",
                            "minimum": 0
                          },
                          "priceCents": {
                            "type": "integer",
                            "description": "Price including VAT",
                            "minimum": 0
                          }
                        },
                        "required": [
                          "id",
                          "coins",
                          "priceCents"
                        ]
                      }
                    },
                    "reference": {
                      "type": "object",
                      "properties": {
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "priceCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "coins",
                        "priceCents"
                      ]
                    },
                    "terms": {
                      "type": "string",
                      "description": "Purchase terms"
                    },
                    "rules": {
                      "type": "object",
                      "properties": {
                        "creatorShareBps": {
                          "type": "integer",
                          "description": "Creator share of each sale in basis points (7000 = 70 %)"
                        },
                        "payoutRateMicros": {
                          "type": "integer",
                          "description": "Payout per earned GAMPEON in millionths of a euro (3800 = €0.0038)"
                        },
                        "payoutCurrency": {
                          "type": "string"
                        },
                        "payoutMinCoins": {
                          "type": "integer",
                          "description": "Minimum payout",
                          "minimum": 0
                        },
                        "holdDays": {
                          "type": "integer",
                          "description": "Days before a creator share can be spent or paid out"
                        },
                        "withdrawalDays": {
                          "type": "integer",
                          "description": "Days to withdraw from an unused purchase"
                        },
                        "monthlyLimitCents": {
                          "type": "integer",
                          "description": "Most an account can spend on GAMPEONS in 30 days",
                          "minimum": 0
                        },
                        "minPrice": {
                          "type": "integer",
                          "description": "Lowest price for an item",
                          "minimum": 0
                        },
                        "maxPrice": {
                          "type": "integer",
                          "description": "Highest price for an item",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "creatorShareBps",
                        "payoutRateMicros",
                        "payoutCurrency",
                        "payoutMinCoins",
                        "holdDays",
                        "withdrawalDays",
                        "monthlyLimitCents",
                        "minPrice",
                        "maxPrice"
                      ]
                    }
                  },
                  "required": [
                    "mode",
                    "provider",
                    "currency",
                    "packages",
                    "reference",
                    "terms",
                    "rules"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/legal/wallet-terms": {
      "get": {
        "operationId": "getWalletTerms",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Purchase, seller and payout terms",
        "description": "Placeholders until legal review.",
        "security": [],
        "responses": {
          "200": {
            "description": "Terms",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "type": "string"
                    },
                    "seller": {
                      "type": "string"
                    },
                    "payout": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "order",
                    "seller",
                    "payout"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/wallet": {
      "get": {
        "operationId": "getWallet",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Your wallet",
        "description": "Balances, status and rules. Unlocks creator shares whose holding period is over.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string",
                      "enum": [
                        "off",
                        "sandbox"
                      ],
                      "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                    },
                    "balances": {
                      "type": "object",
                      "properties": {
                        "wallet": {
                          "type": "integer",
                          "description": "Bought or gifted. Spendable, never paid out",
                          "minimum": 0
                        },
                        "pending": {
                          "type": "integer",
                          "description": "Earned, still in the holding period",
                          "minimum": 0
                        },
                        "earned": {
                          "type": "integer",
                          "description": "Earned. Spendable and can be paid out",
                          "minimum": 0
                        },
                        "payoutHold": {
                          "type": "integer",
                          "description": "Reserved for a payout request",
                          "minimum": 0
                        },
                        "spendable": {
                          "type": "integer",
                          "description": "wallet + earned",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "wallet",
                        "pending",
                        "earned",
                        "payoutHold",
                        "spendable"
                      ]
                    },
                    "locked": {
                      "type": "boolean",
                      "description": "Locked after a chargeback. Buying, spending and payouts are blocked"
                    },
                    "sellerTerms": {
                      "type": "boolean",
                      "description": "Seller terms accepted, prices can be set"
                    },
                    "payoutProgram": {
                      "type": "string",
                      "enum": [
                        "none",
                        "pending",
                        "approved",
                        "rejected",
                        "suspended"
                      ]
                    },
                    "upcoming": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "availableAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "coins": {
                            "type": "integer",
                            "description": "Amount in GAMPEONS",
                            "minimum": 0
                          }
                        },
                        "required": [
                          "availableAt",
                          "coins"
                        ]
                      },
                      "description": "Creator shares that unlock next"
                    },
                    "limits": {
                      "type": "object",
                      "properties": {
                        "monthlyLimitCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "usedCents": {
                          "type": "integer",
                          "description": "Spent or in open orders in the last 30 days",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "monthlyLimitCents",
                        "usedCents"
                      ]
                    },
                    "rules": {
                      "type": "object",
                      "properties": {
                        "creatorShareBps": {
                          "type": "integer",
                          "description": "Creator share of each sale in basis points (7000 = 70 %)"
                        },
                        "payoutRateMicros": {
                          "type": "integer",
                          "description": "Payout per earned GAMPEON in millionths of a euro (3800 = €0.0038)"
                        },
                        "payoutCurrency": {
                          "type": "string"
                        },
                        "payoutMinCoins": {
                          "type": "integer",
                          "description": "Minimum payout",
                          "minimum": 0
                        },
                        "holdDays": {
                          "type": "integer",
                          "description": "Days before a creator share can be spent or paid out"
                        },
                        "withdrawalDays": {
                          "type": "integer",
                          "description": "Days to withdraw from an unused purchase"
                        },
                        "monthlyLimitCents": {
                          "type": "integer",
                          "description": "Most an account can spend on GAMPEONS in 30 days",
                          "minimum": 0
                        },
                        "minPrice": {
                          "type": "integer",
                          "description": "Lowest price for an item",
                          "minimum": 0
                        },
                        "maxPrice": {
                          "type": "integer",
                          "description": "Highest price for an item",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "creatorShareBps",
                        "payoutRateMicros",
                        "payoutCurrency",
                        "payoutMinCoins",
                        "holdDays",
                        "withdrawalDays",
                        "monthlyLimitCents",
                        "minPrice",
                        "maxPrice"
                      ]
                    }
                  },
                  "required": [
                    "mode",
                    "balances",
                    "locked",
                    "sellerTerms",
                    "payoutProgram",
                    "upcoming",
                    "limits",
                    "rules"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/wallet/transactions": {
      "get": {
        "operationId": "listWalletTransactions",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Your transactions",
        "description": "Newest first. `changes` holds only your own accounts; a sale never shows who bought.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 to 100, default 25",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Skip this many",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Transactions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transactions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "memo": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "changes": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer"
                            },
                            "description": "Change per account (wallet, pending, earned, payout_hold)"
                          }
                        },
                        "required": [
                          "id",
                          "kind",
                          "memo",
                          "createdAt",
                          "changes"
                        ]
                      }
                    }
                  },
                  "required": [
                    "transactions"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/wallet/orders": {
      "get": {
        "operationId": "listCoinOrders",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Your GAMPEONS purchases",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Orders",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "packageId": {
                            "type": "string"
                          },
                          "coins": {
                            "type": "integer",
                            "description": "Amount in GAMPEONS",
                            "minimum": 0
                          },
                          "priceCents": {
                            "type": "integer",
                            "description": "Euro cents",
                            "minimum": 0
                          },
                          "currency": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "expired",
                              "paid",
                              "refunded",
                              "charged_back"
                            ]
                          },
                          "provider": {
                            "type": "string",
                            "enum": [
                              "sandbox",
                              "xsolla"
                            ],
                            "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "expiresAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "paidAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "closedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "withdrawableUntil": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Set while the purchase can still be withdrawn"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "packageId",
                          "coins",
                          "priceCents",
                          "currency",
                          "status",
                          "provider",
                          "createdAt",
                          "expiresAt",
                          "paidAt",
                          "closedAt",
                          "withdrawableUntil"
                        ]
                      }
                    }
                  },
                  "required": [
                    "orders"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "post": {
        "operationId": "createCoinOrder",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Start a GAMPEONS purchase",
        "description": "The price comes from the server. The order expires after one hour.\n\nNot available with access tokens.\n\nRate limit: 20 per hour per account.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string",
                      "enum": [
                        "off",
                        "sandbox"
                      ],
                      "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                    },
                    "order": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "packageId": {
                          "type": "string"
                        },
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "priceCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "currency": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "expired",
                            "paid",
                            "refunded",
                            "charged_back"
                          ]
                        },
                        "provider": {
                          "type": "string",
                          "enum": [
                            "sandbox",
                            "xsolla"
                          ],
                          "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "paidAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "closedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "withdrawableUntil": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Set while the purchase can still be withdrawn"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "packageId",
                        "coins",
                        "priceCents",
                        "currency",
                        "status",
                        "provider",
                        "createdAt",
                        "expiresAt",
                        "paidAt",
                        "closedAt",
                        "withdrawableUntil"
                      ]
                    },
                    "checkout": {
                      "type": "object",
                      "properties": {
                        "provider": {
                          "type": "string",
                          "enum": [
                            "sandbox",
                            "xsolla"
                          ],
                          "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                        }
                      },
                      "required": [
                        "provider"
                      ]
                    }
                  },
                  "required": [
                    "mode",
                    "order",
                    "checkout"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `wallet_locked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `monthly_limit`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `terms_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "packageId": {
                    "type": "string"
                  },
                  "terms": {
                    "type": "string",
                    "description": "Exact current purchase terms"
                  }
                },
                "required": [
                  "packageId",
                  "terms"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/wallet/orders/{id}/sandbox-pay": {
      "post": {
        "operationId": "sandboxPayCoinOrder",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Sandbox: simulate a payment",
        "description": "Only in sandbox mode with simulated payments (`provider: sandbox`). Stands in for the payment provider confirming the payment and credits the GAMPEONS.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order ID (`ord_…`)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "packageId": {
                          "type": "string"
                        },
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "priceCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "currency": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "expired",
                            "paid",
                            "refunded",
                            "charged_back"
                          ]
                        },
                        "provider": {
                          "type": "string",
                          "enum": [
                            "sandbox",
                            "xsolla"
                          ],
                          "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "paidAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "closedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "withdrawableUntil": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Set while the purchase can still be withdrawn"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "packageId",
                        "coins",
                        "priceCents",
                        "currency",
                        "status",
                        "provider",
                        "createdAt",
                        "expiresAt",
                        "paidAt",
                        "closedAt",
                        "withdrawableUntil"
                      ]
                    },
                    "wallet": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "off",
                            "sandbox"
                          ],
                          "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                        },
                        "balances": {
                          "type": "object",
                          "properties": {
                            "wallet": {
                              "type": "integer",
                              "description": "Bought or gifted. Spendable, never paid out",
                              "minimum": 0
                            },
                            "pending": {
                              "type": "integer",
                              "description": "Earned, still in the holding period",
                              "minimum": 0
                            },
                            "earned": {
                              "type": "integer",
                              "description": "Earned. Spendable and can be paid out",
                              "minimum": 0
                            },
                            "payoutHold": {
                              "type": "integer",
                              "description": "Reserved for a payout request",
                              "minimum": 0
                            },
                            "spendable": {
                              "type": "integer",
                              "description": "wallet + earned",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "wallet",
                            "pending",
                            "earned",
                            "payoutHold",
                            "spendable"
                          ]
                        },
                        "locked": {
                          "type": "boolean",
                          "description": "Locked after a chargeback. Buying, spending and payouts are blocked"
                        },
                        "sellerTerms": {
                          "type": "boolean",
                          "description": "Seller terms accepted, prices can be set"
                        },
                        "payoutProgram": {
                          "type": "string",
                          "enum": [
                            "none",
                            "pending",
                            "approved",
                            "rejected",
                            "suspended"
                          ]
                        },
                        "upcoming": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "availableAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "coins": {
                                "type": "integer",
                                "description": "Amount in GAMPEONS",
                                "minimum": 0
                              }
                            },
                            "required": [
                              "availableAt",
                              "coins"
                            ]
                          },
                          "description": "Creator shares that unlock next"
                        },
                        "limits": {
                          "type": "object",
                          "properties": {
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Euro cents",
                              "minimum": 0
                            },
                            "usedCents": {
                              "type": "integer",
                              "description": "Spent or in open orders in the last 30 days",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "monthlyLimitCents",
                            "usedCents"
                          ]
                        },
                        "rules": {
                          "type": "object",
                          "properties": {
                            "creatorShareBps": {
                              "type": "integer",
                              "description": "Creator share of each sale in basis points (7000 = 70 %)"
                            },
                            "payoutRateMicros": {
                              "type": "integer",
                              "description": "Payout per earned GAMPEON in millionths of a euro (3800 = €0.0038)"
                            },
                            "payoutCurrency": {
                              "type": "string"
                            },
                            "payoutMinCoins": {
                              "type": "integer",
                              "description": "Minimum payout",
                              "minimum": 0
                            },
                            "holdDays": {
                              "type": "integer",
                              "description": "Days before a creator share can be spent or paid out"
                            },
                            "withdrawalDays": {
                              "type": "integer",
                              "description": "Days to withdraw from an unused purchase"
                            },
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Most an account can spend on GAMPEONS in 30 days",
                              "minimum": 0
                            },
                            "minPrice": {
                              "type": "integer",
                              "description": "Lowest price for an item",
                              "minimum": 0
                            },
                            "maxPrice": {
                              "type": "integer",
                              "description": "Highest price for an item",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "creatorShareBps",
                            "payoutRateMicros",
                            "payoutCurrency",
                            "payoutMinCoins",
                            "holdDays",
                            "withdrawalDays",
                            "monthlyLimitCents",
                            "minPrice",
                            "maxPrice"
                          ]
                        }
                      },
                      "required": [
                        "mode",
                        "balances",
                        "locked",
                        "sellerTerms",
                        "payoutProgram",
                        "upcoming",
                        "limits",
                        "rules"
                      ]
                    }
                  },
                  "required": [
                    "order",
                    "wallet"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `wallet_locked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `order_not_pending`, `order_expired`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/wallet/orders/{id}/checkout": {
      "post": {
        "operationId": "checkoutCoinOrder",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Xsolla: payment page for an order",
        "description": "Only with `provider: xsolla`. Returns the link to the Xsolla Pay Station in test mode (no real money). Asking again returns the same link. The GAMPEONS are credited when Xsolla confirms the payment, then `GET /v1/wallet/orders` shows the order as `paid`.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order ID (`ord_…`)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Payment page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "packageId": {
                          "type": "string"
                        },
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "priceCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "currency": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "expired",
                            "paid",
                            "refunded",
                            "charged_back"
                          ]
                        },
                        "provider": {
                          "type": "string",
                          "enum": [
                            "sandbox",
                            "xsolla"
                          ],
                          "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "paidAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "closedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "withdrawableUntil": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Set while the purchase can still be withdrawn"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "packageId",
                        "coins",
                        "priceCents",
                        "currency",
                        "status",
                        "provider",
                        "createdAt",
                        "expiresAt",
                        "paidAt",
                        "closedAt",
                        "withdrawableUntil"
                      ]
                    },
                    "checkout": {
                      "type": "object",
                      "properties": {
                        "provider": {
                          "const": "xsolla"
                        },
                        "url": {
                          "type": "string",
                          "description": "Xsolla Pay Station (test mode)"
                        },
                        "sandbox": {
                          "const": true
                        }
                      },
                      "required": [
                        "provider",
                        "url",
                        "sandbox"
                      ]
                    }
                  },
                  "required": [
                    "order",
                    "checkout"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `wallet_locked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `order_not_pending`, `order_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Error codes: `payment_unreachable`, `payment_refused`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account"
      }
    },
    "/v1/payments/xsolla/webhook": {
      "post": {
        "operationId": "xsollaWebhook",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Xsolla webhook (called by Xsolla only)",
        "description": "Signed with `Authorization: Signature <sha1(body + secret)>`. Handles `user_validation`, `order_paid` (credits the GAMPEONS once) and `order_canceled` (takes them back like a chargeback). Payments that are not test payments are refused. Errors use Xsolla's format `{ \"error\": { \"code\": \"INVALID_SIGNATURE\" | \"INVALID_USER\" | \"INVALID_PARAMETER\", \"message\": \"…\" } }`. Only active in sandbox mode with `provider: xsolla`.",
        "security": [],
        "responses": {
          "204": {
            "description": "Processed"
          },
          "400": {
            "description": "Error codes: `invalid_field`, `INVALID_USER`, `INVALID_PARAMETER`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `INVALID_SIGNATURE`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `body_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notification_type": {
                    "type": "string",
                    "description": "e.g. `user_validation`, `order_paid`, `order_canceled`"
                  }
                },
                "required": [
                  "notification_type"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/wallet/orders/{id}/withdraw": {
      "post": {
        "operationId": "withdrawCoinOrder",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Withdraw from a purchase",
        "description": "Within 14 days, and only if no GAMPEONS from this purchase have been used.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order ID (`ord_…`)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Withdrawn",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "packageId": {
                          "type": "string"
                        },
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "priceCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "currency": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "expired",
                            "paid",
                            "refunded",
                            "charged_back"
                          ]
                        },
                        "provider": {
                          "type": "string",
                          "enum": [
                            "sandbox",
                            "xsolla"
                          ],
                          "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "paidAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "closedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "withdrawableUntil": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Set while the purchase can still be withdrawn"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "packageId",
                        "coins",
                        "priceCents",
                        "currency",
                        "status",
                        "provider",
                        "createdAt",
                        "expiresAt",
                        "paidAt",
                        "closedAt",
                        "withdrawableUntil"
                      ]
                    },
                    "wallet": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "off",
                            "sandbox"
                          ],
                          "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                        },
                        "balances": {
                          "type": "object",
                          "properties": {
                            "wallet": {
                              "type": "integer",
                              "description": "Bought or gifted. Spendable, never paid out",
                              "minimum": 0
                            },
                            "pending": {
                              "type": "integer",
                              "description": "Earned, still in the holding period",
                              "minimum": 0
                            },
                            "earned": {
                              "type": "integer",
                              "description": "Earned. Spendable and can be paid out",
                              "minimum": 0
                            },
                            "payoutHold": {
                              "type": "integer",
                              "description": "Reserved for a payout request",
                              "minimum": 0
                            },
                            "spendable": {
                              "type": "integer",
                              "description": "wallet + earned",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "wallet",
                            "pending",
                            "earned",
                            "payoutHold",
                            "spendable"
                          ]
                        },
                        "locked": {
                          "type": "boolean",
                          "description": "Locked after a chargeback. Buying, spending and payouts are blocked"
                        },
                        "sellerTerms": {
                          "type": "boolean",
                          "description": "Seller terms accepted, prices can be set"
                        },
                        "payoutProgram": {
                          "type": "string",
                          "enum": [
                            "none",
                            "pending",
                            "approved",
                            "rejected",
                            "suspended"
                          ]
                        },
                        "upcoming": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "availableAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "coins": {
                                "type": "integer",
                                "description": "Amount in GAMPEONS",
                                "minimum": 0
                              }
                            },
                            "required": [
                              "availableAt",
                              "coins"
                            ]
                          },
                          "description": "Creator shares that unlock next"
                        },
                        "limits": {
                          "type": "object",
                          "properties": {
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Euro cents",
                              "minimum": 0
                            },
                            "usedCents": {
                              "type": "integer",
                              "description": "Spent or in open orders in the last 30 days",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "monthlyLimitCents",
                            "usedCents"
                          ]
                        },
                        "rules": {
                          "type": "object",
                          "properties": {
                            "creatorShareBps": {
                              "type": "integer",
                              "description": "Creator share of each sale in basis points (7000 = 70 %)"
                            },
                            "payoutRateMicros": {
                              "type": "integer",
                              "description": "Payout per earned GAMPEON in millionths of a euro (3800 = €0.0038)"
                            },
                            "payoutCurrency": {
                              "type": "string"
                            },
                            "payoutMinCoins": {
                              "type": "integer",
                              "description": "Minimum payout",
                              "minimum": 0
                            },
                            "holdDays": {
                              "type": "integer",
                              "description": "Days before a creator share can be spent or paid out"
                            },
                            "withdrawalDays": {
                              "type": "integer",
                              "description": "Days to withdraw from an unused purchase"
                            },
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Most an account can spend on GAMPEONS in 30 days",
                              "minimum": 0
                            },
                            "minPrice": {
                              "type": "integer",
                              "description": "Lowest price for an item",
                              "minimum": 0
                            },
                            "maxPrice": {
                              "type": "integer",
                              "description": "Highest price for an item",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "creatorShareBps",
                            "payoutRateMicros",
                            "payoutCurrency",
                            "payoutMinCoins",
                            "holdDays",
                            "withdrawalDays",
                            "monthlyLimitCents",
                            "minPrice",
                            "maxPrice"
                          ]
                        }
                      },
                      "required": [
                        "mode",
                        "balances",
                        "locked",
                        "sellerTerms",
                        "payoutProgram",
                        "upcoming",
                        "limits",
                        "rules"
                      ]
                    }
                  },
                  "required": [
                    "order",
                    "wallet"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `order_not_paid`, `withdrawal_period_over`, `already_used`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/resources/{name}/purchase": {
      "post": {
        "operationId": "purchaseResource",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Buy a marketplace item",
        "description": "Send the price you were shown as `expectedPrice`. Bought and gifted GAMPEONS are used first (oldest purchase first), then earned ones. The creator share is credited as `pending`.\n\nNot available with access tokens.\n\nRate limit: 60 per hour per account.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Resource name",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Bought",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "price": {
                      "type": "integer",
                      "description": "Amount in GAMPEONS",
                      "minimum": 0
                    },
                    "owned": {
                      "const": true
                    },
                    "wallet": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "off",
                            "sandbox"
                          ],
                          "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                        },
                        "balances": {
                          "type": "object",
                          "properties": {
                            "wallet": {
                              "type": "integer",
                              "description": "Bought or gifted. Spendable, never paid out",
                              "minimum": 0
                            },
                            "pending": {
                              "type": "integer",
                              "description": "Earned, still in the holding period",
                              "minimum": 0
                            },
                            "earned": {
                              "type": "integer",
                              "description": "Earned. Spendable and can be paid out",
                              "minimum": 0
                            },
                            "payoutHold": {
                              "type": "integer",
                              "description": "Reserved for a payout request",
                              "minimum": 0
                            },
                            "spendable": {
                              "type": "integer",
                              "description": "wallet + earned",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "wallet",
                            "pending",
                            "earned",
                            "payoutHold",
                            "spendable"
                          ]
                        },
                        "locked": {
                          "type": "boolean",
                          "description": "Locked after a chargeback. Buying, spending and payouts are blocked"
                        },
                        "sellerTerms": {
                          "type": "boolean",
                          "description": "Seller terms accepted, prices can be set"
                        },
                        "payoutProgram": {
                          "type": "string",
                          "enum": [
                            "none",
                            "pending",
                            "approved",
                            "rejected",
                            "suspended"
                          ]
                        },
                        "upcoming": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "availableAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "coins": {
                                "type": "integer",
                                "description": "Amount in GAMPEONS",
                                "minimum": 0
                              }
                            },
                            "required": [
                              "availableAt",
                              "coins"
                            ]
                          },
                          "description": "Creator shares that unlock next"
                        },
                        "limits": {
                          "type": "object",
                          "properties": {
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Euro cents",
                              "minimum": 0
                            },
                            "usedCents": {
                              "type": "integer",
                              "description": "Spent or in open orders in the last 30 days",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "monthlyLimitCents",
                            "usedCents"
                          ]
                        },
                        "rules": {
                          "type": "object",
                          "properties": {
                            "creatorShareBps": {
                              "type": "integer",
                              "description": "Creator share of each sale in basis points (7000 = 70 %)"
                            },
                            "payoutRateMicros": {
                              "type": "integer",
                              "description": "Payout per earned GAMPEON in millionths of a euro (3800 = €0.0038)"
                            },
                            "payoutCurrency": {
                              "type": "string"
                            },
                            "payoutMinCoins": {
                              "type": "integer",
                              "description": "Minimum payout",
                              "minimum": 0
                            },
                            "holdDays": {
                              "type": "integer",
                              "description": "Days before a creator share can be spent or paid out"
                            },
                            "withdrawalDays": {
                              "type": "integer",
                              "description": "Days to withdraw from an unused purchase"
                            },
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Most an account can spend on GAMPEONS in 30 days",
                              "minimum": 0
                            },
                            "minPrice": {
                              "type": "integer",
                              "description": "Lowest price for an item",
                              "minimum": 0
                            },
                            "maxPrice": {
                              "type": "integer",
                              "description": "Highest price for an item",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "creatorShareBps",
                            "payoutRateMicros",
                            "payoutCurrency",
                            "payoutMinCoins",
                            "holdDays",
                            "withdrawalDays",
                            "monthlyLimitCents",
                            "minPrice",
                            "maxPrice"
                          ]
                        }
                      },
                      "required": [
                        "mode",
                        "balances",
                        "locked",
                        "sellerTerms",
                        "payoutProgram",
                        "upcoming",
                        "limits",
                        "rules"
                      ]
                    }
                  },
                  "required": [
                    "name",
                    "price",
                    "owned",
                    "wallet"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `wallet_locked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `free_item`, `own_item`, `price_changed`, `already_owned`, `insufficient_funds`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 60 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "60 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expectedPrice": {
                    "type": "integer",
                    "description": "Price shown to the buyer",
                    "minimum": 0
                  }
                },
                "required": [
                  "expectedPrice"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/purchases": {
      "get": {
        "operationId": "listMyPurchases",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Items you bought",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "purchases": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/ResourceListing"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "paid": {
                                "type": "integer",
                                "description": "Amount in GAMPEONS",
                                "minimum": 0
                              },
                              "boughtAt": {
                                "oneOf": [
                                  {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Unix time in milliseconds"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              }
                            },
                            "required": [
                              "paid",
                              "boughtAt"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "purchases"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/wallet/seller-terms": {
      "post": {
        "operationId": "acceptSellerTerms",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Accept the seller terms",
        "description": "Needed before setting a price with `PATCH /v1/resources/:name`.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sellerTerms": {
                      "const": true
                    }
                  },
                  "required": [
                    "sellerTerms"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `terms_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "terms": {
                    "type": "string",
                    "description": "Exact current text of the seller terms from `GET /v1/legal/wallet-terms`"
                  }
                },
                "required": [
                  "terms"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/wallet/payout-program": {
      "post": {
        "operationId": "applyPayoutProgram",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Apply for the payout program",
        "description": "18 or older. In sandbox mode an admin decides; later the identity check of the payout provider does.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Application received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payoutProgram": {
                      "const": "pending"
                    }
                  },
                  "required": [
                    "payoutProgram"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `payout_program_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `payout_program_pending`, `payout_program_approved`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `terms_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "terms": {
                    "type": "string",
                    "description": "Exact current text of the payout program terms from `GET /v1/legal/wallet-terms`"
                  }
                },
                "required": [
                  "terms"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/wallet/payouts": {
      "get": {
        "operationId": "listPayouts",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Your payouts",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Payouts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payouts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "coins": {
                            "type": "integer",
                            "description": "Amount in GAMPEONS",
                            "minimum": 0
                          },
                          "rateMicros": {
                            "type": "integer",
                            "description": "Rate fixed when the payout was requested"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "amountCents": {
                            "type": "integer",
                            "description": "Euro cents",
                            "minimum": 0
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "requested",
                              "paid",
                              "rejected"
                            ]
                          },
                          "provider": {
                            "type": "string"
                          },
                          "reason": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "coins",
                          "rateMicros",
                          "currency",
                          "amountCents",
                          "status",
                          "provider",
                          "reason",
                          "createdAt",
                          "decidedAt"
                        ]
                      }
                    }
                  },
                  "required": [
                    "payouts"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "post": {
        "operationId": "requestPayout",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Request a payout",
        "description": "Only earned GAMPEONS, from the minimum amount, one open request at a time. The rate is fixed at the time of the request.\n\nNot available with access tokens.\n\nRate limit: 5 per day per account.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payout": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "rateMicros": {
                          "type": "integer",
                          "description": "Rate fixed when the payout was requested"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "amountCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "requested",
                            "paid",
                            "rejected"
                          ]
                        },
                        "provider": {
                          "type": "string"
                        },
                        "reason": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "decidedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "coins",
                        "rateMicros",
                        "currency",
                        "amountCents",
                        "status",
                        "provider",
                        "reason",
                        "createdAt",
                        "decidedAt"
                      ]
                    },
                    "wallet": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "off",
                            "sandbox"
                          ],
                          "description": "`off`: payments are switched off. `sandbox`: payments are simulated, no real money moves."
                        },
                        "balances": {
                          "type": "object",
                          "properties": {
                            "wallet": {
                              "type": "integer",
                              "description": "Bought or gifted. Spendable, never paid out",
                              "minimum": 0
                            },
                            "pending": {
                              "type": "integer",
                              "description": "Earned, still in the holding period",
                              "minimum": 0
                            },
                            "earned": {
                              "type": "integer",
                              "description": "Earned. Spendable and can be paid out",
                              "minimum": 0
                            },
                            "payoutHold": {
                              "type": "integer",
                              "description": "Reserved for a payout request",
                              "minimum": 0
                            },
                            "spendable": {
                              "type": "integer",
                              "description": "wallet + earned",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "wallet",
                            "pending",
                            "earned",
                            "payoutHold",
                            "spendable"
                          ]
                        },
                        "locked": {
                          "type": "boolean",
                          "description": "Locked after a chargeback. Buying, spending and payouts are blocked"
                        },
                        "sellerTerms": {
                          "type": "boolean",
                          "description": "Seller terms accepted, prices can be set"
                        },
                        "payoutProgram": {
                          "type": "string",
                          "enum": [
                            "none",
                            "pending",
                            "approved",
                            "rejected",
                            "suspended"
                          ]
                        },
                        "upcoming": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "availableAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "coins": {
                                "type": "integer",
                                "description": "Amount in GAMPEONS",
                                "minimum": 0
                              }
                            },
                            "required": [
                              "availableAt",
                              "coins"
                            ]
                          },
                          "description": "Creator shares that unlock next"
                        },
                        "limits": {
                          "type": "object",
                          "properties": {
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Euro cents",
                              "minimum": 0
                            },
                            "usedCents": {
                              "type": "integer",
                              "description": "Spent or in open orders in the last 30 days",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "monthlyLimitCents",
                            "usedCents"
                          ]
                        },
                        "rules": {
                          "type": "object",
                          "properties": {
                            "creatorShareBps": {
                              "type": "integer",
                              "description": "Creator share of each sale in basis points (7000 = 70 %)"
                            },
                            "payoutRateMicros": {
                              "type": "integer",
                              "description": "Payout per earned GAMPEON in millionths of a euro (3800 = €0.0038)"
                            },
                            "payoutCurrency": {
                              "type": "string"
                            },
                            "payoutMinCoins": {
                              "type": "integer",
                              "description": "Minimum payout",
                              "minimum": 0
                            },
                            "holdDays": {
                              "type": "integer",
                              "description": "Days before a creator share can be spent or paid out"
                            },
                            "withdrawalDays": {
                              "type": "integer",
                              "description": "Days to withdraw from an unused purchase"
                            },
                            "monthlyLimitCents": {
                              "type": "integer",
                              "description": "Most an account can spend on GAMPEONS in 30 days",
                              "minimum": 0
                            },
                            "minPrice": {
                              "type": "integer",
                              "description": "Lowest price for an item",
                              "minimum": 0
                            },
                            "maxPrice": {
                              "type": "integer",
                              "description": "Highest price for an item",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "creatorShareBps",
                            "payoutRateMicros",
                            "payoutCurrency",
                            "payoutMinCoins",
                            "holdDays",
                            "withdrawalDays",
                            "monthlyLimitCents",
                            "minPrice",
                            "maxPrice"
                          ]
                        }
                      },
                      "required": [
                        "mode",
                        "balances",
                        "locked",
                        "sellerTerms",
                        "payoutProgram",
                        "upcoming",
                        "limits",
                        "rules"
                      ]
                    }
                  },
                  "required": [
                    "payout",
                    "wallet"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `payout_program_required`, `wallet_locked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `below_minimum`, `payout_open`, `insufficient_funds`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 5 per day per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "5 per day per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "coins": {
                    "type": "integer",
                    "minimum": 1
                  }
                },
                "required": [
                  "coins"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/payout-program": {
      "get": {
        "operationId": "listPayoutProgram",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Payout program applications",
        "description": "Requires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default `pending`",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "rejected",
                "suspended",
                "none"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accounts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "since": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "status",
                          "since"
                        ]
                      }
                    }
                  },
                  "required": [
                    "accounts"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin"
      }
    },
    "/v1/admin/payout-program/{handle}": {
      "post": {
        "operationId": "decidePayoutProgram",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Decide on a payout program application",
        "description": "Never on your own account. `reject` and `suspend` need a reason.\n\nRequires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "payoutProgram": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "handle",
                    "payoutProgram"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`, `four_eyes_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `no_application`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject",
                      "suspend"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "description": "Reason, 5 to 500 characters. Stored in the audit log.",
                    "minLength": 5,
                    "maxLength": 500
                  }
                },
                "required": [
                  "decision"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/payouts": {
      "get": {
        "operationId": "listPayoutRequests",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Payout requests",
        "description": "Requires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default `requested`",
            "schema": {
              "type": "string",
              "enum": [
                "requested",
                "paid",
                "rejected"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Payouts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payouts": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "coins": {
                                "type": "integer",
                                "description": "Amount in GAMPEONS",
                                "minimum": 0
                              },
                              "rateMicros": {
                                "type": "integer",
                                "description": "Rate fixed when the payout was requested"
                              },
                              "currency": {
                                "type": "string"
                              },
                              "amountCents": {
                                "type": "integer",
                                "description": "Euro cents",
                                "minimum": 0
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "requested",
                                  "paid",
                                  "rejected"
                                ]
                              },
                              "provider": {
                                "type": "string"
                              },
                              "reason": {
                                "oneOf": [
                                  {
                                    "type": "string"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "createdAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "decidedAt": {
                                "oneOf": [
                                  {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Unix time in milliseconds"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              }
                            },
                            "required": [
                              "id",
                              "coins",
                              "rateMicros",
                              "currency",
                              "amountCents",
                              "status",
                              "provider",
                              "reason",
                              "createdAt",
                              "decidedAt"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "handle": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "handle"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "payouts"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin"
      }
    },
    "/v1/admin/payouts/{id}/decision": {
      "post": {
        "operationId": "decidePayout",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Approve or decline a payout",
        "description": "Never your own request. In sandbox mode no money moves.\n\nRequires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payout ID (`pay_…`)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payout": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "coins": {
                          "type": "integer",
                          "description": "Amount in GAMPEONS",
                          "minimum": 0
                        },
                        "rateMicros": {
                          "type": "integer",
                          "description": "Rate fixed when the payout was requested"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "amountCents": {
                          "type": "integer",
                          "description": "Euro cents",
                          "minimum": 0
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "requested",
                            "paid",
                            "rejected"
                          ]
                        },
                        "provider": {
                          "type": "string"
                        },
                        "reason": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "decidedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "coins",
                        "rateMicros",
                        "currency",
                        "amountCents",
                        "status",
                        "provider",
                        "reason",
                        "createdAt",
                        "decidedAt"
                      ]
                    }
                  },
                  "required": [
                    "payout"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`, `four_eyes_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `payout_not_open`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "description": "Reason, 5 to 500 characters. Stored in the audit log.",
                    "minLength": 5,
                    "maxLength": 500
                  }
                },
                "required": [
                  "decision"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/wallet/grants": {
      "post": {
        "operationId": "grantCoins",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Gift GAMPEONS to an account",
        "description": "Never to yourself. Gifted GAMPEONS can be spent but never paid out.\n\nRequires the role admin.\n\nNot available with access tokens.\n\nRate limit: 50 per hour per admin.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Gifted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "coins": {
                      "type": "integer",
                      "description": "Amount in GAMPEONS",
                      "minimum": 0
                    }
                  },
                  "required": [
                    "handle",
                    "coins"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`, `four_eyes_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 50 per hour per admin. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin",
        "x-gampeon-rate-limit": "50 per hour per admin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string"
                  },
                  "coins": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100000
                  },
                  "reason": {
                    "type": "string",
                    "description": "Reason, 5 to 500 characters. Stored in the audit log.",
                    "minLength": 5,
                    "maxLength": 500
                  }
                },
                "required": [
                  "handle",
                  "coins",
                  "reason"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/wallet/orders/{id}/chargeback": {
      "post": {
        "operationId": "sandboxChargeback",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Sandbox: simulate a chargeback",
        "description": "Takes back the rest of the purchase, removes items bought with it, reverses creator shares still on hold and locks the wallet.\n\nRequires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order ID (`ord_…`)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Required. 8 to 100 characters (`A-Z a-z 0-9 _ - : .`). The same key with the same body returns the stored answer (`replayed: true`) without booking again.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Charged back",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "clawedBack": {
                      "type": "integer",
                      "description": "Amount in GAMPEONS",
                      "minimum": 0
                    },
                    "revokedItems": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "order",
                    "status",
                    "clawedBack",
                    "revokedItems"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `idempotency_key_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `order_not_paid`, `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `payments_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Reason, 5 to 500 characters. Stored in the audit log.",
                    "minLength": 5,
                    "maxLength": 500
                  }
                },
                "required": [
                  "reason"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/wallet/{handle}/unlock": {
      "post": {
        "operationId": "unlockWallet",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Unlock a wallet",
        "description": "Requires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Unlocked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "locked": {
                      "const": false
                    }
                  },
                  "required": [
                    "handle",
                    "locked"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`, `four_eyes_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Reason, 5 to 500 characters. Stored in the audit log.",
                    "minLength": 5,
                    "maxLength": 500
                  }
                },
                "required": [
                  "reason"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/admin/wallet/{handle}": {
      "get": {
        "operationId": "getWalletForSupport",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Wallet of an account (support)",
        "description": "Requires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "balances": {
                      "type": "object",
                      "properties": {
                        "wallet": {
                          "type": "integer",
                          "description": "Bought or gifted. Spendable, never paid out",
                          "minimum": 0
                        },
                        "pending": {
                          "type": "integer",
                          "description": "Earned, still in the holding period",
                          "minimum": 0
                        },
                        "earned": {
                          "type": "integer",
                          "description": "Earned. Spendable and can be paid out",
                          "minimum": 0
                        },
                        "payoutHold": {
                          "type": "integer",
                          "description": "Reserved for a payout request",
                          "minimum": 0
                        },
                        "spendable": {
                          "type": "integer",
                          "description": "wallet + earned",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "wallet",
                        "pending",
                        "earned",
                        "payoutHold",
                        "spendable"
                      ]
                    },
                    "payoutProgram": {
                      "type": "string"
                    },
                    "locked": {
                      "type": "boolean"
                    },
                    "lockedReason": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "orders": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "packageId": {
                            "type": "string"
                          },
                          "coins": {
                            "type": "integer",
                            "description": "Amount in GAMPEONS",
                            "minimum": 0
                          },
                          "priceCents": {
                            "type": "integer",
                            "description": "Euro cents",
                            "minimum": 0
                          },
                          "currency": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "expired",
                              "paid",
                              "refunded",
                              "charged_back"
                            ]
                          },
                          "provider": {
                            "type": "string",
                            "enum": [
                              "sandbox",
                              "xsolla"
                            ],
                            "description": "`sandbox`: simulated payment with `POST /v1/wallet/orders/:id/sandbox-pay`. `xsolla`: Xsolla test payment through `POST /v1/wallet/orders/:id/checkout`."
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "expiresAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "paidAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "closedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "withdrawableUntil": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Set while the purchase can still be withdrawn"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "packageId",
                          "coins",
                          "priceCents",
                          "currency",
                          "status",
                          "provider",
                          "createdAt",
                          "expiresAt",
                          "paidAt",
                          "closedAt",
                          "withdrawableUntil"
                        ]
                      }
                    },
                    "payouts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "coins": {
                            "type": "integer",
                            "description": "Amount in GAMPEONS",
                            "minimum": 0
                          },
                          "rateMicros": {
                            "type": "integer",
                            "description": "Rate fixed when the payout was requested"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "amountCents": {
                            "type": "integer",
                            "description": "Euro cents",
                            "minimum": 0
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "requested",
                              "paid",
                              "rejected"
                            ]
                          },
                          "provider": {
                            "type": "string"
                          },
                          "reason": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "coins",
                          "rateMicros",
                          "currency",
                          "amountCents",
                          "status",
                          "provider",
                          "reason",
                          "createdAt",
                          "decidedAt"
                        ]
                      }
                    }
                  },
                  "required": [
                    "handle",
                    "balances",
                    "payoutProgram",
                    "locked",
                    "lockedReason",
                    "orders",
                    "payouts"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin"
      }
    },
    "/v1/admin/ledger/verify": {
      "get": {
        "operationId": "verifyLedger",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Check the books",
        "description": "Every transaction sums to zero, balances match their entries, both hash chains are intact, no user account is negative, and each wallet matches its open purchases.\n\nRequires the role admin.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "transactions": {
                      "type": "integer"
                    },
                    "entries": {
                      "type": "integer"
                    },
                    "userCoins": {
                      "type": "integer",
                      "description": "Amount in GAMPEONS",
                      "minimum": 0
                    },
                    "issued": {
                      "type": "integer",
                      "description": "Issued minus taken back"
                    },
                    "problems": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "ok",
                    "transactions",
                    "entries",
                    "userCoins",
                    "issued",
                    "problems"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin"
      }
    },
    "/v1/admin/audit": {
      "get": {
        "operationId": "listAuditLog",
        "tags": [
          "GAMPEONS"
        ],
        "summary": "Audit log",
        "description": "Requires the role admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 to 200, default 50",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Skip this many",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Entries, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "actorId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "action": {
                            "type": "string"
                          },
                          "target": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "details": {
                            "type": "object",
                            "additionalProperties": {}
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          }
                        },
                        "required": [
                          "id",
                          "actorId",
                          "action",
                          "target",
                          "details",
                          "createdAt"
                        ]
                      }
                    }
                  },
                  "required": [
                    "entries"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "admin"
      }
    },
    "/v1/social/suggestions": {
      "get": {
        "operationId": "getSocialSuggestions",
        "tags": [
          "Social"
        ],
        "summary": "Who to follow",
        "description": "Creators with public content, studios and games, most followed first. Without the ones you already follow and accounts blocked in either direction.\n\nNot available with access tokens.",
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Suggestions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "creators": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "followers": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "followers"
                        ]
                      }
                    },
                    "studios": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "tagline": {
                            "type": "string"
                          },
                          "followers": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "slug",
                          "name",
                          "tagline",
                          "followers"
                        ]
                      }
                    },
                    "projects": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "tagline": {
                            "type": "string"
                          },
                          "genre": {
                            "type": "string"
                          },
                          "followers": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "slug",
                          "title",
                          "tagline",
                          "genre",
                          "followers"
                        ]
                      }
                    }
                  },
                  "required": [
                    "creators",
                    "studios",
                    "projects"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/social/{type}/{key}": {
      "get": {
        "operationId": "getSocialSummary",
        "tags": [
          "Social"
        ],
        "summary": "Follower counts and your relationship",
        "description": "Counts are public. With login, `viewer` describes your relationship (follow, friendship, block). Hidden studios and projects count as not found.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`user`, `studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Handle (user) or slug (studio, project)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "key": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "followers": {
                      "type": "integer"
                    },
                    "following": {
                      "type": "integer",
                      "description": "Accounts only"
                    },
                    "friends": {
                      "type": "integer",
                      "description": "Accounts only"
                    },
                    "viewer": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "self": {
                              "type": "boolean",
                              "description": "This is your own account"
                            },
                            "following": {
                              "type": "boolean"
                            },
                            "canFollow": {
                              "type": "boolean"
                            },
                            "friend": {
                              "oneOf": [
                                {
                                  "type": "string",
                                  "enum": [
                                    "none",
                                    "outgoing",
                                    "incoming",
                                    "friends"
                                  ],
                                  "description": "Accounts only"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "blocked": {
                              "type": "boolean",
                              "description": "You blocked this account"
                            },
                            "canFriend": {
                              "type": "boolean",
                              "description": "A friend request (or accepting one) is possible"
                            }
                          },
                          "required": [
                            "self",
                            "following",
                            "canFollow",
                            "friend",
                            "blocked",
                            "canFriend"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "type",
                    "key",
                    "name",
                    "followers",
                    "viewer"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/social/{type}/{key}/follow": {
      "put": {
        "operationId": "follow",
        "tags": [
          "Social"
        ],
        "summary": "Follow an account, studio or game",
        "description": "Idempotent. Accounts get a `new_follower` notification at most once per person and week.\n\nNot available with access tokens.\n\nRate limit: 60 follow changes per hour per account, 2,000 follows in total.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`user`, `studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Handle (user) or slug (studio, project)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Following",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "following": {
                      "const": true
                    },
                    "followers": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "following",
                    "followers"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `follow_limit`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 60 follow changes per hour per account, 2,000 follows in total. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "60 follow changes per hour per account, 2,000 follows in total"
      },
      "delete": {
        "operationId": "unfollow",
        "tags": [
          "Social"
        ],
        "summary": "Unfollow",
        "description": "Not available with access tokens.\n\nRate limit: 60 follow changes per hour per account.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`user`, `studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Handle (user) or slug (studio, project)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Not following",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "following": {
                      "const": false
                    },
                    "followers": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "following",
                    "followers"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 60 follow changes per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "60 follow changes per hour per account"
      }
    },
    "/v1/social/{type}/{key}/followers": {
      "get": {
        "operationId": "listFollowers",
        "tags": [
          "Social"
        ],
        "summary": "Followers",
        "description": "Up to 50 per page, newest first. For accounts: Visible if the account’s `showConnections` allows it (otherwise `403 private`). Other people only appear if their own setting allows you to see them; your own lists are complete. For studios and games, only people whose own setting allows it appear.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`user`, `studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Handle (user) or slug (studio, project)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Entries to skip",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Followers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer",
                      "description": "All followers, including hidden ones"
                    },
                    "followers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "since": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "mutual": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "since",
                          "mutual"
                        ]
                      }
                    }
                  },
                  "required": [
                    "total",
                    "followers"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `private`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/social/{type}/{key}/following": {
      "get": {
        "operationId": "listFollowing",
        "tags": [
          "Social"
        ],
        "summary": "What an account follows",
        "description": "Accounts only (`type` = `user`). Visible if the account’s `showConnections` allows it (otherwise `403 private`). Other people only appear if their own setting allows you to see them; your own lists are complete.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`user`, `studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Handle (user) or slug (studio, project)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Followed accounts, studios and games",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "users": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SocialTarget"
                      }
                    },
                    "studios": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SocialTarget"
                      }
                    },
                    "projects": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SocialTarget"
                      }
                    }
                  },
                  "required": [
                    "users",
                    "studios",
                    "projects"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `private`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/social/{type}/{key}/friends": {
      "get": {
        "operationId": "listFriends",
        "tags": [
          "Social"
        ],
        "summary": "Friends of an account",
        "description": "Accounts only (`type` = `user`). Visible if the account’s `showConnections` allows it (otherwise `403 private`). Other people only appear if their own setting allows you to see them; your own lists are complete.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "`user`, `studio` or `project`",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "studio",
                "project"
              ]
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Handle (user) or slug (studio, project)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Friends",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "friends": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "mutual": {
                            "type": "boolean",
                            "description": "Also a friend of yours"
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "mutual"
                        ]
                      }
                    }
                  },
                  "required": [
                    "total",
                    "friends"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `private`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/me/friends": {
      "get": {
        "operationId": "getMyFriends",
        "tags": [
          "Social"
        ],
        "summary": "Your friends, requests and blocks",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Lists",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "friends": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "handle": {
                                "type": "string"
                              },
                              "displayName": {
                                "type": "string"
                              },
                              "since": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              }
                            },
                            "required": [
                              "handle",
                              "displayName",
                              "since"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "online": {
                                "type": "boolean",
                                "description": "Active in the last 3 minutes, if this friend shows it"
                              },
                              "points": {
                                "type": "integer",
                                "description": "Achievement points (level = points / 100 + 1)"
                              }
                            },
                            "required": [
                              "online",
                              "points"
                            ]
                          }
                        ]
                      }
                    },
                    "incoming": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "since": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "since"
                        ]
                      }
                    },
                    "outgoing": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "since": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "since"
                        ]
                      }
                    },
                    "blocked": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "since": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "since"
                        ]
                      }
                    },
                    "limits": {
                      "type": "object",
                      "properties": {
                        "friends": {
                          "type": "integer"
                        },
                        "pendingOutgoing": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "friends",
                        "pendingOutgoing"
                      ]
                    }
                  },
                  "required": [
                    "friends",
                    "incoming",
                    "outgoing",
                    "blocked",
                    "limits"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/friends/requests": {
      "post": {
        "operationId": "sendFriendRequest",
        "tags": [
          "Social"
        ],
        "summary": "Send a friend request",
        "description": "If the other person already asked you, you are friends right away (`status: friends`). Blocks and the other person’s settings both answer `403 not_allowed`, so a block is not revealed.\n\nNot available with access tokens.\n\nRate limit: 20 per hour per account, 100 open requests, 1,000 friends.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Request sent (200 if it already existed or was accepted)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "friends"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_target`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_friends`, `too_many_pending`, `friend_limit`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per hour per account, 100 open requests, 1,000 friends. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 per hour per account, 100 open requests, 1,000 friends",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string",
                    "description": "Handle, with or without @"
                  }
                },
                "required": [
                  "handle"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/friends/requests/{handle}/accept": {
      "post": {
        "operationId": "acceptFriendRequest",
        "tags": [
          "Social"
        ],
        "summary": "Accept a friend request",
        "description": "Not available with access tokens.\n\nRate limit: 120 answers per hour per account.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Friends",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "const": "friends"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `friend_limit`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 answers per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 answers per hour per account"
      }
    },
    "/v1/friends/requests/{handle}": {
      "delete": {
        "operationId": "deleteFriendRequest",
        "tags": [
          "Social"
        ],
        "summary": "Decline or cancel a friend request",
        "description": "Not available with access tokens.\n\nRate limit: 120 answers per hour per account.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 answers per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 answers per hour per account"
      }
    },
    "/v1/friends/{handle}": {
      "delete": {
        "operationId": "removeFriend",
        "tags": [
          "Social"
        ],
        "summary": "Remove a friend",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/blocks/{handle}": {
      "put": {
        "operationId": "blockAccount",
        "tags": [
          "Social"
        ],
        "summary": "Block an account",
        "description": "Removes friendship, open requests and follows in both directions and the account’s likes on your posts. The blocked account can no longer follow you, send requests or like your posts.\n\nNot available with access tokens.\n\nRate limit: 60 per hour per account.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Blocked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "blocked": {
                      "const": true
                    }
                  },
                  "required": [
                    "blocked"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_target`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 60 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "60 per hour per account"
      },
      "delete": {
        "operationId": "unblockAccount",
        "tags": [
          "Social"
        ],
        "summary": "Unblock an account",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the account",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Unblocked"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/social-settings": {
      "get": {
        "operationId": "getSocialSettings",
        "tags": [
          "Social"
        ],
        "summary": "Your privacy settings",
        "description": "Defaults: friend requests from `everyone`, connections visible to `friends`. Under 16 (and without a birth month): friend requests only from `friends_of_friends`, connections `private`, and only these stricter levels can be chosen. `options` lists what you can choose.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Settings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "friendRequests": {
                          "type": "string",
                          "enum": [
                            "everyone",
                            "friends_of_friends",
                            "nobody"
                          ],
                          "description": "Who can send you friend requests"
                        },
                        "showConnections": {
                          "type": "string",
                          "enum": [
                            "public",
                            "friends",
                            "private"
                          ],
                          "description": "Who can see your friends and follows. Also decides whether you appear in other people’s lists"
                        },
                        "showOnline": {
                          "type": "boolean",
                          "description": "Your friends see when you are online (active on the website or in the launcher in the last 3 minutes). Nobody else sees it"
                        }
                      },
                      "required": [
                        "friendRequests",
                        "showConnections",
                        "showOnline"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "ageGroup": {
                          "type": "string",
                          "enum": [
                            "protected",
                            "adult"
                          ]
                        },
                        "options": {
                          "type": "object",
                          "properties": {
                            "friendRequests": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "showConnections": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          },
                          "required": [
                            "friendRequests",
                            "showConnections"
                          ]
                        }
                      },
                      "required": [
                        "ageGroup",
                        "options"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "patch": {
        "operationId": "updateSocialSettings",
        "tags": [
          "Social"
        ],
        "summary": "Change your privacy settings",
        "description": "Not available with access tokens.\n\nRate limit: 120 changes per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "friendRequests": {
                      "type": "string",
                      "enum": [
                        "everyone",
                        "friends_of_friends",
                        "nobody"
                      ],
                      "description": "Who can send you friend requests"
                    },
                    "showConnections": {
                      "type": "string",
                      "enum": [
                        "public",
                        "friends",
                        "private"
                      ],
                      "description": "Who can see your friends and follows. Also decides whether you appear in other people’s lists"
                    },
                    "showOnline": {
                      "type": "boolean",
                      "description": "Your friends see when you are online (active on the website or in the launcher in the last 3 minutes). Nobody else sees it"
                    }
                  },
                  "required": [
                    "friendRequests",
                    "showConnections",
                    "showOnline"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `age_restricted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 changes per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 changes per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "friendRequests": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "friends_of_friends",
                      "nobody"
                    ],
                    "description": "Who can send you friend requests"
                  },
                  "showConnections": {
                    "type": "string",
                    "enum": [
                      "public",
                      "friends",
                      "private"
                    ],
                    "description": "Who can see your friends and follows. Also decides whether you appear in other people’s lists"
                  },
                  "showOnline": {
                    "type": "boolean",
                    "description": "Your friends see when you are online (active on the website or in the launcher in the last 3 minutes). Nobody else sees it"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/posts": {
      "post": {
        "operationId": "createPost",
        "tags": [
          "Social"
        ],
        "summary": "Write a post",
        "description": "Text with up to 3 links, up to 4 photos (upload them first with `POST /v1/me/post-images`), a GIF (`gif`, from `GET /v1/gifs`) or a shared post (`share`). `audience` sets who sees it (default `public`, under 16 `friends`); studio and game posts are always public. `commentMode` sets who can comment. `@handle` mentions notify that account (under 16 only from friends). The first link gets a preview card (title and site name). `as` posts in the name of a studio (owner or admin) or a game (people who can edit it). Control and formatting characters are rejected; more than two empty lines are shortened.\n\nNot available with access tokens.\n\nRate limit: 10 per hour and 50 per day per account; 3 per day in the first 24 hours of an account; no identical text within 24 hours.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `too_many_links`, `invalid_image_ref`, `invalid_gif`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `not_shareable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `duplicate_post`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour and 50 per day per account; 3 per day in the first 24 hours of an account; no identical text within 24 hours. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Error codes: `gifs_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `gifs_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per hour and 50 per day per account; 3 per day in the first 24 hours of an account; no identical text within 24 hours",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "Up to 1,000 characters and 3 links. Required unless there are photos, a GIF or a share",
                    "maxLength": 1000
                  },
                  "as": {
                    "type": "string",
                    "description": "`studio:SLUG` or `project:SLUG`. Omit to post as yourself."
                  },
                  "audience": {
                    "type": "string",
                    "enum": [
                      "public",
                      "friends",
                      "private"
                    ]
                  },
                  "commentMode": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "friends",
                      "off"
                    ],
                    "description": "`friends` only for your own posts"
                  },
                  "images": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "File name from `POST /v1/me/post-images`"
                    },
                    "maxItems": 4
                  },
                  "share": {
                    "type": "integer",
                    "description": "ID of a public post to share. Shares of shares share the original"
                  },
                  "gif": {
                    "type": "string",
                    "description": "GIF ID (`id` from `GET /v1/gifs`). Not together with photos or a share"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listPosts",
        "tags": [
          "Social"
        ],
        "summary": "Public posts",
        "description": "With `author`: posts of one account, studio or game that you can see (audience, blocks). On the first page a pinned post comes separately in `pinned` and is left out of `posts`. Without `author`: news from all studios and games (not from single accounts).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "author",
            "in": "query",
            "required": false,
            "description": "`user:HANDLE`, `studio:SLUG` or `project:SLUG`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Cursor `next` from the previous page",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 50 (default 30)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 30
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Posts, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pinned": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Post"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    },
                    "next": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "Cursor for the next page"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "pinned",
                    "posts",
                    "next"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/me/post-images": {
      "post": {
        "operationId": "uploadPostImage",
        "tags": [
          "Social"
        ],
        "summary": "Upload a photo for a post",
        "description": "PNG, JPEG or WebP up to 2 MB and at least 64×64 pixels. Metadata (EXIF, GPS) is removed. The photo is only visible once it belongs to a post; photos that were never posted are deleted after 24 hours.\n\nNot available with access tokens.\n\nRate limit: 30 images per hour per account; up to 20 photos that aren’t posted yet.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Uploaded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "type": "string",
                      "description": "Use it in `images` of `POST /v1/posts`"
                    },
                    "url": {
                      "type": "string"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "file",
                    "url",
                    "width",
                    "height"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `too_many_pending_images`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 images per hour per account; up to 20 photos that aren’t posted yet. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 images per hour per account; up to 20 photos that aren’t posted yet",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      }
    },
    "/v1/me/post-images/{file}": {
      "delete": {
        "operationId": "discardPostImage",
        "tags": [
          "Social"
        ],
        "summary": "Discard a photo you haven’t posted",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "description": "File name from the upload",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Discarded"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `image_in_use`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/link-preview": {
      "get": {
        "operationId": "getLinkPreview",
        "tags": [
          "Social"
        ],
        "summary": "Preview card for a link",
        "description": "Title and site name of an https page, for the post form. We don’t load images or descriptions from other sites. Previews are kept for 7 days.\n\nNot available with access tokens.\n\nRate limit: 60 per hour per account.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "https link, up to 500 characters",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Card (title `null` if the page has none or can’t be reached)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkCard"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 60 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "60 per hour per account"
      }
    },
    "/v1/gifs/status": {
      "get": {
        "operationId": "getGifStatus",
        "tags": [
          "Social"
        ],
        "summary": "Whether GIFs are available",
        "description": "GIFs come from KLIPY and are only on when the server has a KLIPY key. The portal hides the GIF button otherwise.",
        "security": [],
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "provider": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "`klipy`"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "enabled",
                    "provider"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/gifs": {
      "get": {
        "operationId": "searchGifs",
        "tags": [
          "Social"
        ],
        "summary": "Search GIFs",
        "description": "Search KLIPY with `q`, or trending GIFs without it, 24 per page. Accounts under 16 get the strictest content filter. The API asks KLIPY on your behalf without your account ID, and images are delivered through `GET /v1/gifs/:id/:size`, so your browser never talks to KLIPY. Show “Search KLIPY” in the search field (KLIPY’s attribution rule).\n\nNot available with access tokens.\n\nRate limit: 300 per hour per account.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search words, up to 100 characters. Empty: trending",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1 to 20",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "GIFs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gifs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GifResult"
                      }
                    },
                    "next": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "description": "Next page"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "provider": {
                      "type": "string",
                      "description": "`klipy`"
                    }
                  },
                  "required": [
                    "gifs",
                    "next",
                    "provider"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 300 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Error codes: `gifs_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `gifs_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "300 per hour per account"
      }
    },
    "/v1/gifs/{slug}/{size}": {
      "get": {
        "operationId": "getGifImage",
        "tags": [
          "Social"
        ],
        "summary": "GIF image",
        "description": "The animated image (WebP, or GIF if KLIPY has no WebP) from KLIPY, delivered by the API. `sm` is the small preview for the picker, `md` the size in posts and comments. Only GIFs that came from a search or are attached to a post. Cached for a day.\n\nRate limit: 1,500 per 10 minutes per IP address.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "GIF ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "size",
            "in": "path",
            "required": true,
            "description": "`sm` or `md`",
            "schema": {
              "type": "string",
              "enum": [
                "sm",
                "md"
              ]
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The image",
            "content": {
              "image/webp, image/gif": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 1,500 per 10 minutes per IP address. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "1,500 per 10 minutes per IP address"
      }
    },
    "/v1/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "tags": [
          "Social"
        ],
        "summary": "One post",
        "description": "Removed posts are only visible to their author, with `removed` and the reason. Posts for friends or only the author count as not found for others.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      },
      "patch": {
        "operationId": "updatePost",
        "tags": [
          "Social"
        ],
        "summary": "Edit a post",
        "description": "Text, audience or who can comment. A new text sets `editedAt`, and only newly mentioned accounts are notified. Photos and shares stay as they are.\n\nNot available with access tokens.\n\nRate limit: 120 changes per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `too_many_links`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `post_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 changes per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 changes per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 1000
                  },
                  "audience": {
                    "type": "string",
                    "enum": [
                      "public",
                      "friends",
                      "private"
                    ]
                  },
                  "commentMode": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "friends",
                      "off"
                    ]
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "tags": [
          "Social"
        ],
        "summary": "Delete a post",
        "description": "Author, studio owners and admins, or people who can edit the game. Deleting is final and takes photos, comments and reactions with it. A copy of reported text stays with the report for the moderation record. Shares of it show the original as unavailable.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/posts/{id}/pin": {
      "put": {
        "operationId": "pinPost",
        "tags": [
          "Social"
        ],
        "summary": "Pin a post to the top of the timeline",
        "description": "One pinned post per account, studio and game; pinning another one replaces it.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Pinned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pinned": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "pinned"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `post_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "delete": {
        "operationId": "unpinPost",
        "tags": [
          "Social"
        ],
        "summary": "Unpin a post",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Not pinned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pinned": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "pinned"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/posts/{id}/reaction": {
      "put": {
        "operationId": "reactToPost",
        "tags": [
          "Social"
        ],
        "summary": "React to a post",
        "description": "One reaction per person; a new one replaces the old one. No notification is sent for reactions.\n\nNot available with access tokens.\n\nRate limit: 300 per hour per account (together with likes).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "like",
                            "love",
                            "haha",
                            "wow",
                            "sad",
                            "angry"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reactions": {
                      "$ref": "#/components/schemas/Reactions"
                    },
                    "liked": {
                      "type": "boolean",
                      "description": "Older field: any reaction"
                    },
                    "likes": {
                      "type": "integer",
                      "description": "Older field: all reactions"
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions",
                    "liked",
                    "likes"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 300 per hour per account (together with likes). Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "300 per hour per account (together with likes)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reaction": {
                    "type": "string",
                    "enum": [
                      "like",
                      "love",
                      "haha",
                      "wow",
                      "sad",
                      "angry"
                    ]
                  }
                },
                "required": [
                  "reaction"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "removePostReaction",
        "tags": [
          "Social"
        ],
        "summary": "Remove your reaction",
        "description": "Not available with access tokens.\n\nRate limit: 300 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "like",
                            "love",
                            "haha",
                            "wow",
                            "sad",
                            "angry"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reactions": {
                      "$ref": "#/components/schemas/Reactions"
                    },
                    "liked": {
                      "type": "boolean",
                      "description": "Older field: any reaction"
                    },
                    "likes": {
                      "type": "integer",
                      "description": "Older field: all reactions"
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions",
                    "liked",
                    "likes"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 300 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "300 per hour per account"
      }
    },
    "/v1/posts/{id}/like": {
      "put": {
        "operationId": "likePost",
        "tags": [
          "Social"
        ],
        "summary": "Like a post",
        "description": "Same as the reaction `like`.\n\nNot available with access tokens.\n\nRate limit: 300 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Liked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "like",
                            "love",
                            "haha",
                            "wow",
                            "sad",
                            "angry"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reactions": {
                      "$ref": "#/components/schemas/Reactions"
                    },
                    "liked": {
                      "type": "boolean",
                      "description": "Older field: any reaction"
                    },
                    "likes": {
                      "type": "integer",
                      "description": "Older field: all reactions"
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions",
                    "liked",
                    "likes"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 300 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "300 per hour per account"
      },
      "delete": {
        "operationId": "unlikePost",
        "tags": [
          "Social"
        ],
        "summary": "Remove your like",
        "description": "Removes any reaction.\n\nNot available with access tokens.\n\nRate limit: 300 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Not liked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "like",
                            "love",
                            "haha",
                            "wow",
                            "sad",
                            "angry"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reactions": {
                      "$ref": "#/components/schemas/Reactions"
                    },
                    "liked": {
                      "type": "boolean",
                      "description": "Older field: any reaction"
                    },
                    "likes": {
                      "type": "integer",
                      "description": "Older field: all reactions"
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions",
                    "liked",
                    "likes"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 300 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "300 per hour per account"
      }
    },
    "/v1/posts/{id}/reactions": {
      "get": {
        "operationId": "listPostReactions",
        "tags": [
          "Social"
        ],
        "summary": "Who reacted to a post",
        "description": "Counts are complete. People only appear if their own setting (`showConnections`) lets you see them. Up to 100, newest first.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "reaction",
            "in": "query",
            "required": false,
            "description": "Only this reaction",
            "schema": {
              "type": "string",
              "enum": [
                "like",
                "love",
                "haha",
                "wow",
                "sad",
                "angry"
              ]
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reactions",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Reactions"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "people": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "handle": {
                                "type": "string"
                              },
                              "displayName": {
                                "type": "string"
                              },
                              "avatar": {
                                "oneOf": [
                                  {
                                    "type": "string"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "reaction": {
                                "type": "string",
                                "enum": [
                                  "like",
                                  "love",
                                  "haha",
                                  "wow",
                                  "sad",
                                  "angry"
                                ]
                              }
                            },
                            "required": [
                              "handle",
                              "displayName",
                              "avatar",
                              "reaction"
                            ]
                          }
                        }
                      },
                      "required": [
                        "people"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/posts/{id}/comments": {
      "get": {
        "operationId": "listComments",
        "tags": [
          "Social"
        ],
        "summary": "Comments on a post",
        "description": "Top-level comments: newest page first (`before`), each page in reading order (oldest first). Replies (`parent`): oldest first, continue with `after`. Without blocked accounts; removed comments only for their author.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "parent",
            "in": "query",
            "required": false,
            "description": "Top-level comment ID: list its replies",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Top-level comments: `next` from the previous page",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Replies: `next` from the previous page",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 50 (default 20)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Comments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer",
                      "description": "All visible comments and replies"
                    },
                    "comments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Comment"
                      }
                    },
                    "next": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "total",
                    "comments",
                    "next"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      },
      "post": {
        "operationId": "createComment",
        "tags": [
          "Social"
        ],
        "summary": "Comment on a post",
        "description": "Text, a GIF (`gif`, from `GET /v1/gifs`) or both. `parentId` answers a comment; answers to answers are placed under the top-level comment. Notifies the author of the post, the person you answer and mentioned accounts, each at most once. On posts of accounts under 16 only friends can comment.\n\nNot available with access tokens.\n\nRate limit: 30 per hour and 200 per day per account; 10 per day in the first 24 hours; no identical comment on the same post within an hour.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Comment"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `too_many_links`, `invalid_gif`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `comments_off`, `not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `duplicate_comment`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour and 200 per day per account; 10 per day in the first 24 hours; no identical comment on the same post within an hour. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Error codes: `gifs_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `gifs_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour and 200 per day per account; 10 per day in the first 24 hours; no identical comment on the same post within an hour",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "Up to 1,000 characters and 3 links. Required unless there is a GIF",
                    "maxLength": 1000
                  },
                  "gif": {
                    "type": "string",
                    "description": "GIF ID (`id` from `GET /v1/gifs`)"
                  },
                  "parentId": {
                    "type": "integer"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/comments/{id}": {
      "patch": {
        "operationId": "updateComment",
        "tags": [
          "Social"
        ],
        "summary": "Edit your comment",
        "description": "Not available with access tokens.\n\nRate limit: 120 changes per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Comment ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Comment"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `too_many_links`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `comment_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 changes per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 changes per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "Up to 1,000 characters. Can be empty if the comment has a GIF",
                    "maxLength": 1000
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteComment",
        "tags": [
          "Social"
        ],
        "summary": "Delete a comment",
        "description": "The person who wrote it, or whoever manages the post. Replies are deleted with it.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Comment ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/comments/{id}/reaction": {
      "put": {
        "operationId": "reactToComment",
        "tags": [
          "Social"
        ],
        "summary": "React to a comment",
        "description": "Not available with access tokens.\n\nRate limit: 300 per hour per account (together with post reactions).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Comment ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "like",
                            "love",
                            "haha",
                            "wow",
                            "sad",
                            "angry"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reactions": {
                      "$ref": "#/components/schemas/Reactions"
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 300 per hour per account (together with post reactions). Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "300 per hour per account (together with post reactions)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reaction": {
                    "type": "string",
                    "enum": [
                      "like",
                      "love",
                      "haha",
                      "wow",
                      "sad",
                      "angry"
                    ]
                  }
                },
                "required": [
                  "reaction"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "removeCommentReaction",
        "tags": [
          "Social"
        ],
        "summary": "Remove your reaction to a comment",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Comment ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "like",
                            "love",
                            "haha",
                            "wow",
                            "sad",
                            "angry"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reactions": {
                      "$ref": "#/components/schemas/Reactions"
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/feed": {
      "get": {
        "operationId": "getFeed",
        "tags": [
          "Social"
        ],
        "summary": "Your feed",
        "description": "Chronological, without ranking: your posts, posts of friends and of accounts, studios and games you follow, and new versions of marketplace items by creators and games you follow. Without blocked accounts and hidden pages.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Cursor `next` from the previous page",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 50 (default 30)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 30
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Feed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FeedItem"
                      }
                    },
                    "next": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "sources": {
                      "type": "integer",
                      "description": "Number of follows and friends"
                    }
                  },
                  "required": [
                    "items",
                    "next",
                    "sources"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/legal/social-report-categories": {
      "get": {
        "operationId": "getSocialReportCategories",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report categories for posts",
        "security": [],
        "responses": {
          "200": {
            "description": "Categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "illegal_content",
                          "harassment",
                          "hate_speech",
                          "violence_threat",
                          "child_safety",
                          "spam",
                          "copyright",
                          "other"
                        ]
                      }
                    },
                    "anonymous": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Categories that need no name and email"
                    }
                  },
                  "required": [
                    "categories",
                    "anonymous"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/posts/{id}/reports": {
      "post": {
        "operationId": "reportPost",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report a post",
        "description": "Notice under Art. 16 DSA, also without an account. Name and email are required except for `child_safety`; when logged in they come from your account. A copy of the text (and references to its photos) is kept with the report.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you already reported this post)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "illegal_content",
                      "harassment",
                      "hate_speech",
                      "violence_threat",
                      "child_safety",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/comments/{id}/reports": {
      "post": {
        "operationId": "reportComment",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report a comment",
        "description": "Same rules as reporting a post. A copy of the comment is kept with the report.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP (together with other reports).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Comment ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you already reported this comment)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP (together with other reports). Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-rate-limit": "10 per hour per IP (together with other reports)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "illegal_content",
                      "harassment",
                      "hate_speech",
                      "violence_threat",
                      "child_safety",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/comments/{id}/decision": {
      "post": {
        "operationId": "moderateComment",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Remove or restore a comment without a report",
        "description": "Always with a statement of reasons, which the person who commented sees (Art. 17 DSA).\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Comment ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_removed`, `not_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "restore"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/social-reports": {
      "get": {
        "operationId": "listPostReports",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Reports about posts, comments and images",
        "description": "Up to 200, `urgent` ones (`child_safety`, `violence_threat`) first, then oldest first.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default: open",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "actioned",
                "rejected"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reports",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "post",
                              "comment",
                              "media"
                            ]
                          },
                          "postId": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "description": "The post, also for comments"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "commentId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "snapshot": {
                            "type": "string",
                            "description": "Reported text"
                          },
                          "category": {
                            "type": "string"
                          },
                          "explanation": {
                            "type": "string"
                          },
                          "reporterName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "authorHandle": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "authorName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "removedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "deleted": {
                            "type": "boolean",
                            "description": "Deleted by the author"
                          },
                          "urgent": {
                            "type": "boolean"
                          },
                          "media": {
                            "type": "object",
                            "properties": {
                              "file": {
                                "type": "string"
                              },
                              "slot": {
                                "type": "string"
                              },
                              "url": {
                                "type": "string",
                                "description": "View with a moderator session"
                              }
                            },
                            "required": [
                              "file",
                              "slot",
                              "url"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "kind",
                          "postId",
                          "commentId",
                          "snapshot",
                          "category",
                          "explanation",
                          "reporterName",
                          "status",
                          "createdAt",
                          "authorHandle",
                          "authorName",
                          "removedAt",
                          "deleted",
                          "urgent"
                        ]
                      }
                    }
                  },
                  "required": [
                    "reports"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/social-reports/{id}/decision": {
      "post": {
        "operationId": "decidePostReport",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide on a report",
        "description": "`remove` hides the post, comment or image, closes all open reports about it and informs the author (Art. 17 DSA) and the reporting people (Art. 16(5)). `reject` keeps it and informs the reporting person.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Report ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "reject"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/posts/{id}/decision": {
      "post": {
        "operationId": "moderatePost",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Remove or restore a post without a report",
        "description": "For example during a spam wave. Always with a statement of reasons, which the author sees.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Post ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_removed`, `not_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "restore"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/social/appeals": {
      "post": {
        "operationId": "appealPostRemoval",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Appeal a removal",
        "description": "One appeal per decision (Art. 20 DSA). Someone from moderation decides and explains the outcome.\n\nNot available with access tokens.\n\nRate limit: 10 per day per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Appeal received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appealId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "appealId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `appeal_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per day per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per day per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decisionId": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "decisionId",
                  "text"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/social-decisions": {
      "get": {
        "operationId": "listMyPostDecisions",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Removals of your posts, comments and images",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 100, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "decisionId": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "post",
                              "comment",
                              "media"
                            ]
                          },
                          "postId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "commentId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "mediaFile": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "snapshot": {
                            "type": "string"
                          },
                          "statementOfReasons": {
                            "type": "string"
                          },
                          "legalGround": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "appealId": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "appealStatus": {
                            "oneOf": [
                              {
                                "type": "string",
                                "enum": [
                                  "open",
                                  "upheld",
                                  "reversed"
                                ]
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "outcomeReasons": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "decisionId",
                          "kind",
                          "postId",
                          "commentId",
                          "mediaFile",
                          "snapshot",
                          "statementOfReasons",
                          "legalGround",
                          "createdAt",
                          "appealId",
                          "appealStatus",
                          "outcomeReasons",
                          "decidedAt"
                        ]
                      }
                    }
                  },
                  "required": [
                    "decisions"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/moderation/social-appeals": {
      "get": {
        "operationId": "listPostAppeals",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Appeals about posts and images",
        "description": "`ownDecision` shows whether you removed the post or image yourself, so someone else can review it. Image appeals carry `media`.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default: open",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "upheld",
                "reversed"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Appeals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "text": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "outcomeReasons": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "decisionId": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "post",
                              "comment",
                              "media"
                            ]
                          },
                          "snapshot": {
                            "type": "string"
                          },
                          "statementOfReasons": {
                            "type": "string"
                          },
                          "legalGround": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "removedAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "postId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "commentId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "authorHandle": {
                            "type": "string"
                          },
                          "authorName": {
                            "type": "string"
                          },
                          "ownDecision": {
                            "type": "boolean"
                          },
                          "media": {
                            "type": "object",
                            "properties": {
                              "file": {
                                "type": "string"
                              },
                              "slot": {
                                "type": "string"
                              },
                              "url": {
                                "type": "string",
                                "description": "View with a moderator session"
                              }
                            },
                            "required": [
                              "file",
                              "slot",
                              "url"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "text",
                          "status",
                          "createdAt",
                          "decidedAt",
                          "outcomeReasons",
                          "decisionId",
                          "kind",
                          "snapshot",
                          "statementOfReasons",
                          "legalGround",
                          "removedAt",
                          "postId",
                          "commentId",
                          "authorHandle",
                          "authorName",
                          "ownDecision"
                        ]
                      }
                    }
                  },
                  "required": [
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/social-appeals/{id}/decision": {
      "post": {
        "operationId": "decidePostAppeal",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide on an appeal about a post",
        "description": "`reverse` restores the post unless another removal still applies. The author gets the reasons and the notice about out-of-court dispute settlement (Art. 21 DSA).\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Appeal ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appealId": {
                      "type": "string"
                    },
                    "outcome": {
                      "type": "string"
                    },
                    "restored": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "appealId",
                    "outcome",
                    "restored"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "uphold",
                      "reverse"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "outcome",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/social-stats": {
      "get": {
        "operationId": "getPostModerationStats",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Numbers for the transparency report (posts)",
        "description": "Requires the role moderator or admin.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "category": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "category",
                          "status",
                          "n"
                        ]
                      }
                    },
                    "decisions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "action": {
                            "type": "string"
                          },
                          "onNotice": {
                            "type": "integer",
                            "description": "1 if based on a report"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "action",
                          "onNotice",
                          "n"
                        ]
                      }
                    },
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "status": {
                            "type": "string"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "status",
                          "n"
                        ]
                      }
                    }
                  },
                  "required": [
                    "reports",
                    "decisions",
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/tokens/scopes": {
      "get": {
        "operationId": "listTokenScopes",
        "tags": [
          "Access tokens"
        ],
        "summary": "Scopes and templates for access tokens",
        "description": "All scopes an access token can have, the templates the portal offers and the limits. Which route needs which scope is in `x-gampeon-token-scope` on each operation. Routes without it can never be used with an access token.",
        "security": [],
        "responses": {
          "200": {
            "description": "Scopes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "enum": [
                              "profile:read",
                              "projects:read",
                              "projects:write",
                              "marketplace:read",
                              "marketplace:write",
                              "servers:read"
                            ],
                            "description": "Scope"
                          },
                          "description": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "name",
                          "description"
                        ]
                      }
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "profile:read",
                                "projects:read",
                                "projects:write",
                                "marketplace:read",
                                "marketplace:write",
                                "servers:read"
                              ],
                              "description": "Scope"
                            }
                          }
                        },
                        "required": [
                          "id",
                          "title",
                          "scopes"
                        ]
                      }
                    },
                    "defaultExpiresInDays": {
                      "type": "integer"
                    },
                    "maxExpiresInDays": {
                      "type": "integer"
                    },
                    "maxActiveTokens": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "scopes",
                    "templates",
                    "defaultExpiresInDays",
                    "maxExpiresInDays",
                    "maxActiveTokens"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/token": {
      "get": {
        "operationId": "getCurrentToken",
        "tags": [
          "Access tokens"
        ],
        "summary": "The access token you are using",
        "description": "Name, scopes and expiry of the access token sent with this request. Works with every valid access token, whatever its scopes. MCP clients and tools use it to check a token and to offer only what it allows. With a portal session: `400 not_an_access_token`.\n\nAccess tokens: every valid token, no scope needed.",
        "security": [
          {
            "session": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "prefix": {
                      "type": "string",
                      "description": "First characters of the token"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "profile:read",
                          "projects:read",
                          "projects:write",
                          "marketplace:read",
                          "marketplace:write",
                          "servers:read"
                        ],
                        "description": "Scope"
                      }
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "name",
                    "prefix",
                    "scopes",
                    "createdAt",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `not_an_access_token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "any"
      }
    },
    "/v1/me/tokens": {
      "get": {
        "operationId": "listMyTokens",
        "tags": [
          "Access tokens"
        ],
        "summary": "Your access tokens",
        "description": "Active, expired and revoked tokens of the last 90 days. The token itself is never shown again. Only with a portal session. Access tokens cannot manage tokens (`403 token_not_allowed`).\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tokens",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Token ID, used to revoke it and to filter the activity log"
                          },
                          "name": {
                            "type": "string"
                          },
                          "prefix": {
                            "type": "string",
                            "description": "First characters of the token (`gpat_` and 4 more), to recognise it"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "profile:read",
                                "projects:read",
                                "projects:write",
                                "marketplace:read",
                                "marketplace:write",
                                "servers:read"
                              ],
                              "description": "Scope"
                            }
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "expiresAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "lastUsedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "revokedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "expired",
                              "revoked"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "prefix",
                          "scopes",
                          "createdAt",
                          "expiresAt",
                          "lastUsedAt",
                          "revokedAt",
                          "status"
                        ]
                      }
                    }
                  },
                  "required": [
                    "tokens"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "post": {
        "operationId": "createMyToken",
        "tags": [
          "Access tokens"
        ],
        "summary": "Create an access token",
        "description": "Creates a personal access token for an AI assistant, an MCP client or a tool. The answer contains the token **once**; only a hash is stored. A notification `token_created` is added to the account. Only with a portal session. Access tokens cannot manage tokens (`403 token_not_allowed`).\n\nNot available with access tokens.\n\nRate limit: 10 per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Token created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "token": {
                      "type": "string",
                      "description": "The token, `gpat_` and 43 characters. Shown only in this answer."
                    },
                    "prefix": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "profile:read",
                          "projects:read",
                          "projects:write",
                          "marketplace:read",
                          "marketplace:write",
                          "servers:read"
                        ],
                        "description": "Scope"
                      }
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "token",
                    "prefix",
                    "scopes",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `token_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name to recognise the token, for example \"Claude Desktop\"",
                    "minLength": 1,
                    "maxLength": 64
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "profile:read",
                        "projects:read",
                        "projects:write",
                        "marketplace:read",
                        "marketplace:write",
                        "servers:read"
                      ],
                      "description": "Scope"
                    },
                    "description": "At least one scope",
                    "minItems": 1
                  },
                  "expiresInDays": {
                    "type": "integer",
                    "description": "Validity in days, default 30",
                    "minimum": 1,
                    "maximum": 365
                  }
                },
                "required": [
                  "name",
                  "scopes"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/tokens/{id}": {
      "delete": {
        "operationId": "revokeMyToken",
        "tags": [
          "Access tokens"
        ],
        "summary": "Revoke an access token",
        "description": "The token stops working immediately. Only with a portal session. Access tokens cannot manage tokens (`403 token_not_allowed`).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Token ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/token-events": {
      "get": {
        "operationId": "listMyTokenEvents",
        "tags": [
          "Access tokens"
        ],
        "summary": "Activity of your access tokens",
        "description": "What your tokens did in the last 90 days (\"AI activity\" in the portal): route, result, client and time. Never request or response contents. Repeated reads of the same route are logged at most every 10 minutes. `outcome: allowed` means the token passed the scope check. Only with a portal session. Access tokens cannot manage tokens (`403 token_not_allowed`).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "description": "Only events of this token ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Event ID from `next` of the previous page",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 to 200, default 50",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Events, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "tokenId": {
                            "type": "string"
                          },
                          "tokenName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "route": {
                            "type": "string",
                            "description": "Route like `POST /v1/projects`, or `token.created` / `token.revoked`"
                          },
                          "target": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Requested path"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "outcome": {
                            "type": "string",
                            "enum": [
                              "allowed",
                              "denied"
                            ]
                          },
                          "reason": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Why it was denied, for example `missing_scope:projects:write` or `route_not_allowed`"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "client": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Client name as sent in `X-Gampeon-Client` or the user agent (self-reported)"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "tool": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "MCP tool as sent in `X-Gampeon-Tool` (self-reported)"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "ip": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          }
                        },
                        "required": [
                          "id",
                          "tokenId",
                          "tokenName",
                          "route",
                          "target",
                          "outcome",
                          "reason",
                          "client",
                          "tool",
                          "ip",
                          "createdAt"
                        ]
                      }
                    },
                    "next": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "description": "Pass as `before` for the next page"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "events",
                    "next"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/auth/providers": {
      "get": {
        "operationId": "listSignInProviders",
        "tags": [
          "Accounts"
        ],
        "summary": "Sign-in providers",
        "description": "Which providers can be used to log in and to connect accounts. Steam uses OpenID 2.0, Discord OAuth2 with the scope `identify`.",
        "security": [],
        "responses": {
          "200": {
            "description": "Providers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "providers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "steam",
                              "discord"
                            ],
                            "description": "Sign-in provider"
                          },
                          "name": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "name"
                        ]
                      },
                      "description": "Providers that are switched on"
                    }
                  },
                  "required": [
                    "providers"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/auth/{provider}/start": {
      "post": {
        "operationId": "startSignIn",
        "tags": [
          "Accounts"
        ],
        "summary": "Start a sign-in with Steam or Discord",
        "description": "Returns the address to send the browser to. The provider redirects back to the portal with `state` (and `code` for Discord, `openid.*` parameters for Steam), and the portal completes the sign-in with `POST /v1/auth/{provider}/callback`. The sign-in is valid for 10 minutes and can be used once.\n\n`mode: \"link\"` connects the provider to the account you are logged in with (portal session required; access tokens are refused).\n\nNot available with access tokens.\n\nRate limit: 30 per 15 minutes per IP.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "description": "Sign-in provider: `steam` or `discord`",
            "schema": {
              "type": "string",
              "enum": [
                "steam",
                "discord"
              ],
              "description": "Sign-in provider"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Address at the provider",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "url",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `provider_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per 15 minutes per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-rate-limit": "30 per 15 minutes per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "login",
                      "link"
                    ],
                    "description": "`login`: log in or sign up. `link`: connect to your account."
                  },
                  "nonce": {
                    "type": "string",
                    "description": "Random value (22 to 128 characters `A-Z a-z 0-9 _ -`) that the client keeps in the browser session and sends again with the callback. Binds the sign-in to the browser that started it.",
                    "pattern": "^[A-Za-z0-9_-]{22,128}$"
                  }
                },
                "required": [
                  "mode",
                  "nonce"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/auth/{provider}/callback": {
      "post": {
        "operationId": "completeSignIn",
        "tags": [
          "Accounts"
        ],
        "summary": "Complete a sign-in with Steam or Discord",
        "description": "Checks the answer of the provider and then, depending on the mode and on whether the provider account is already connected:\n\n- `result: \"login\"`: a session for the connected account.\n- `result: \"signup\"`: the provider account is not connected yet. Use `ticket` with `POST /v1/auth/signup` within 30 minutes. An existing account with the same email address is never connected automatically: log in to it and connect the provider instead.\n- `result: \"linked\"`: connected to your account (mode `link`, with the session of the same account).\n\nNo access tokens of the provider are stored. Discord tokens are revoked right after reading the user ID.\n\nNot available with access tokens.\n\nRate limit: 30 per 15 minutes per IP.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "description": "Sign-in provider: `steam` or `discord`",
            "schema": {
              "type": "string",
              "enum": [
                "steam",
                "discord"
              ],
              "description": "Sign-in provider"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Signed in, sign-up needed, or connected",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "result": {
                          "const": "login"
                        },
                        "token": {
                          "type": "string",
                          "description": "Bearer token for the `Authorization` header"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        }
                      },
                      "required": [
                        "result",
                        "token",
                        "expiresAt"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "result": {
                          "const": "signup"
                        },
                        "ticket": {
                          "type": "string",
                          "description": "One-time ticket for `POST /v1/auth/signup`"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "provider": {
                          "type": "string",
                          "enum": [
                            "steam",
                            "discord"
                          ],
                          "description": "Sign-in provider"
                        },
                        "name": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "result",
                        "ticket",
                        "expiresAt",
                        "provider",
                        "name"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "result": {
                          "const": "linked"
                        },
                        "identity": {
                          "type": "object",
                          "properties": {
                            "provider": {
                              "type": "string",
                              "enum": [
                                "steam",
                                "discord"
                              ],
                              "description": "Sign-in provider"
                            },
                            "name": {
                              "oneOf": [
                                {
                                  "type": "string",
                                  "description": "Name at the provider. Steam names need a Steam Web API key on the server; without it `null`."
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "url": {
                              "type": "string",
                              "description": "Profile page at the provider",
                              "format": "uri"
                            },
                            "showOnProfile": {
                              "type": "boolean",
                              "description": "Shown on your public profile. Never for accounts under 16."
                            },
                            "createdAt": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            "lastLoginAt": {
                              "oneOf": [
                                {
                                  "type": "integer",
                                  "format": "int64",
                                  "description": "Unix time in milliseconds"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "provider",
                            "name",
                            "url",
                            "showOnProfile",
                            "createdAt",
                            "lastLoginAt"
                          ]
                        }
                      },
                      "required": [
                        "result",
                        "identity"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_state`, `invalid_response`, `cancelled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `email_unverified`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `provider_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `identity_taken`, `already_linked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per 15 minutes per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Error codes: `provider_error`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-rate-limit": "30 per 15 minutes per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "state": {
                    "type": "string",
                    "description": "`state` from the provider's redirect",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  },
                  "nonce": {
                    "type": "string",
                    "description": "Random value (22 to 128 characters `A-Z a-z 0-9 _ -`) that the client keeps in the browser session and sends again with the callback. Binds the sign-in to the browser that started it.",
                    "pattern": "^[A-Za-z0-9_-]{22,128}$"
                  },
                  "code": {
                    "type": "string",
                    "description": "Discord only: `code` from the redirect"
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Steam only: all `openid.*` query parameters of the redirect, unchanged"
                  }
                },
                "required": [
                  "state",
                  "nonce"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/auth/signup": {
      "post": {
        "operationId": "signUpWithProvider",
        "tags": [
          "Accounts"
        ],
        "summary": "Create an account after a sign-in with a provider",
        "description": "Creates an account connected to the provider account from the ticket. Same rules as `POST /v1/accounts` (minimum age 13, accepting the current terms, email confirmation), but without a password. A password can be set later with \"Forgot password\". When email confirmation is off, the answer also contains a session.\n\nRate limit: 5 per hour per IP (shared with `POST /v1/accounts`).",
        "security": [],
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "email": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "handle": {
                      "type": "string"
                    },
                    "verificationRequired": {
                      "type": "boolean",
                      "description": "An email with a confirmation link was sent"
                    },
                    "token": {
                      "type": "string",
                      "description": "Only when no confirmation is needed"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "id",
                    "email",
                    "displayName",
                    "handle",
                    "verificationRequired"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_ticket`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `signup_closed`, `too_young`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `email_taken`, `handle_taken`, `identity_taken`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `terms_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 5 per hour per IP (shared with `POST /v1/accounts`). Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "5 per hour per IP (shared with `POST /v1/accounts`)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ticket": {
                    "type": "string",
                    "description": "`ticket` from `POST /v1/auth/{provider}/callback`",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "displayName": {
                    "type": "string",
                    "description": "3 to 32 letters, digits, spaces, `_` or `-`",
                    "minLength": 3,
                    "maxLength": 32
                  },
                  "handle": {
                    "type": "string",
                    "description": "Optional wished handle",
                    "pattern": "^$|^[a-z0-9_]{3,24}$"
                  },
                  "birthMonth": {
                    "type": "string",
                    "description": "Birth month as `YYYY-MM`. Minimum age 13.",
                    "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
                  },
                  "acceptTerms": {
                    "type": "string",
                    "description": "Current version from `GET /v1/legal/terms`. Sending it means the person accepts the Terms of Use and the Community Guidelines in that version.",
                    "maxLength": 40
                  }
                },
                "required": [
                  "ticket",
                  "email",
                  "displayName",
                  "birthMonth",
                  "acceptTerms"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/identities": {
      "get": {
        "operationId": "listMyIdentities",
        "tags": [
          "Accounts"
        ],
        "summary": "Your connected accounts",
        "description": "Steam and Discord accounts connected to your account, whether you have a password, and which providers can be connected.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Connected accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "identities": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "provider": {
                            "type": "string",
                            "enum": [
                              "steam",
                              "discord"
                            ],
                            "description": "Sign-in provider"
                          },
                          "name": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Name at the provider. Steam names need a Steam Web API key on the server; without it `null`."
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "url": {
                            "type": "string",
                            "description": "Profile page at the provider",
                            "format": "uri"
                          },
                          "showOnProfile": {
                            "type": "boolean",
                            "description": "Shown on your public profile. Never for accounts under 16."
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "lastLoginAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "provider",
                          "name",
                          "url",
                          "showOnProfile",
                          "createdAt",
                          "lastLoginAt"
                        ]
                      }
                    },
                    "hasPassword": {
                      "type": "boolean"
                    },
                    "providers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "steam",
                              "discord"
                            ],
                            "description": "Sign-in provider"
                          },
                          "name": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "name"
                        ]
                      },
                      "description": "Providers that are switched on"
                    },
                    "ageGroup": {
                      "type": "string",
                      "enum": [
                        "protected",
                        "adult"
                      ]
                    }
                  },
                  "required": [
                    "identities",
                    "hasPassword",
                    "providers",
                    "ageGroup"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/identities/{provider}": {
      "patch": {
        "operationId": "updateMyIdentity",
        "tags": [
          "Accounts"
        ],
        "summary": "Show a connected account on your profile",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "description": "Sign-in provider: `steam` or `discord`",
            "schema": {
              "type": "string",
              "enum": [
                "steam",
                "discord"
              ],
              "description": "Sign-in provider"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "string",
                      "enum": [
                        "steam",
                        "discord"
                      ],
                      "description": "Sign-in provider"
                    },
                    "name": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "Name at the provider. Steam names need a Steam Web API key on the server; without it `null`."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "url": {
                      "type": "string",
                      "description": "Profile page at the provider",
                      "format": "uri"
                    },
                    "showOnProfile": {
                      "type": "boolean",
                      "description": "Shown on your public profile. Never for accounts under 16."
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "lastLoginAt": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "provider",
                    "name",
                    "url",
                    "showOnProfile",
                    "createdAt",
                    "lastLoginAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `age_restricted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "showOnProfile": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "showOnProfile"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteMyIdentity",
        "tags": [
          "Accounts"
        ],
        "summary": "Disconnect a connected account",
        "description": "Refused with `409 last_login_method` if it is the only way to log in (no password and no other connected account).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "description": "Sign-in provider: `steam` or `discord`",
            "schema": {
              "type": "string",
              "enum": [
                "steam",
                "discord"
              ],
              "description": "Sign-in provider"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Disconnected"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `last_login_method`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/avatar": {
      "put": {
        "operationId": "uploadAvatar",
        "tags": [
          "Profiles"
        ],
        "summary": "Upload your profile picture",
        "description": "PNG, JPEG or WebP up to 2 MB, no animation. The server removes all metadata (EXIF including GPS position, XMP, IPTC, comments, embedded thumbnails) and gives the file a new random name. Replacing an image deletes the old one. At least 128×128 pixels, roughly square (crop before uploading).\n\nNot available with access tokens.\n\nRate limit: 30 uploads per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored and set as the current image",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Image path relative to the API (`/v1/media/…`)"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "url",
                    "width",
                    "height"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`, `animated_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 uploads per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 uploads per hour per account",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteAvatar",
        "tags": [
          "Profiles"
        ],
        "summary": "Remove your profile picture",
        "description": "Removes the current image and deletes the file. Pages fall back to the default artwork.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/cover": {
      "put": {
        "operationId": "uploadCover",
        "tags": [
          "Profiles"
        ],
        "summary": "Upload your profile cover",
        "description": "PNG, JPEG or WebP up to 2 MB, no animation. The server removes all metadata (EXIF including GPS position, XMP, IPTC, comments, embedded thumbnails) and gives the file a new random name. Replacing an image deletes the old one. At least 640×160 pixels, wide (1.5:1 to 8:1, for example 1920×480).\n\nNot available with access tokens.\n\nRate limit: 30 uploads per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored and set as the current image",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Image path relative to the API (`/v1/media/…`)"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "url",
                    "width",
                    "height"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`, `animated_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 uploads per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 uploads per hour per account",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteCover",
        "tags": [
          "Profiles"
        ],
        "summary": "Remove your profile cover",
        "description": "Removes the current image and deletes the file. Pages fall back to the default artwork.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/studios/{slug}/logo": {
      "put": {
        "operationId": "uploadStudioLogo",
        "tags": [
          "Studios"
        ],
        "summary": "Upload the studio logo",
        "description": "PNG, JPEG or WebP up to 2 MB, no animation. The server removes all metadata (EXIF including GPS position, XMP, IPTC, comments, embedded thumbnails) and gives the file a new random name. Replacing an image deletes the old one. At least 128×128 pixels, roughly square. Owner and admins only.\n\nNot available with access tokens.\n\nRate limit: 30 uploads per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored and set as the current image",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Image path relative to the API (`/v1/media/…`)"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "url",
                    "width",
                    "height"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`, `animated_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 uploads per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 uploads per hour per account",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteStudioLogo",
        "tags": [
          "Studios"
        ],
        "summary": "Remove the studio logo",
        "description": "Removes the current image and deletes the file. Pages fall back to the default artwork.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/studios/{slug}/cover": {
      "put": {
        "operationId": "uploadStudioCover",
        "tags": [
          "Studios"
        ],
        "summary": "Upload the studio cover",
        "description": "PNG, JPEG or WebP up to 2 MB, no animation. The server removes all metadata (EXIF including GPS position, XMP, IPTC, comments, embedded thumbnails) and gives the file a new random name. Replacing an image deletes the old one. At least 640×160 pixels, wide (1.5:1 to 8:1). Owner and admins only.\n\nNot available with access tokens.\n\nRate limit: 30 uploads per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored and set as the current image",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Image path relative to the API (`/v1/media/…`)"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "url",
                    "width",
                    "height"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`, `animated_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 uploads per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 uploads per hour per account",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteStudioCover",
        "tags": [
          "Studios"
        ],
        "summary": "Remove the studio cover",
        "description": "Removes the current image and deletes the file. Pages fall back to the default artwork.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/projects/{slug}/cover": {
      "put": {
        "operationId": "uploadProjectCover",
        "tags": [
          "Projects"
        ],
        "summary": "Upload the project key art",
        "description": "PNG, JPEG or WebP up to 2 MB, no animation. The server removes all metadata (EXIF including GPS position, XMP, IPTC, comments, embedded thumbnails) and gives the file a new random name. Replacing an image deletes the old one. At least 640×320 pixels, landscape (1.2:1 to 4:1, for example 1920×1080). Shown on the project page and on tiles. Everyone who can edit the project.\n\nNot available with access tokens.\n\nRate limit: 30 uploads per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Stored and set as the current image",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Image path relative to the API (`/v1/media/…`)"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "url",
                    "width",
                    "height"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`, `animated_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 uploads per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 uploads per hour per account",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteProjectCover",
        "tags": [
          "Projects"
        ],
        "summary": "Remove the project key art",
        "description": "Removes the current image and deletes the file. Pages fall back to the default artwork.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/media/{file}": {
      "get": {
        "operationId": "getMedia",
        "tags": [
          "Profiles"
        ],
        "summary": "Profile picture, cover, logo or key art",
        "description": "Only delivered while the image is current and not removed by moderation. Cached for 10 minutes.",
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "description": "`<32 hex characters>.<png|jpg|webp>`, from an image path like `avatar` or `cover`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The image",
            "content": {
              "image/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/media/{file}/reports": {
      "post": {
        "operationId": "reportMedia",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report an image",
        "description": "Notice under Art. 16 DSA for a profile picture, cover, studio logo or project key art, also without an account. Same rules as reporting a post.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP.",
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "description": "`<32 hex characters>.<png|jpg|webp>`, from an image path like `avatar` or `cover`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you already reported this image)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "illegal_content",
                      "harassment",
                      "hate_speech",
                      "violence_threat",
                      "child_safety",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/media/{file}/decision": {
      "post": {
        "operationId": "moderateMedia",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Remove or restore an image without a report",
        "description": "Removing takes the image off the profile, studio or project and stops delivering it; the file is kept for an appeal. Always with a statement of reasons, which the uploader sees.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "description": "`<32 hex characters>.<png|jpg|webp>`, from an image path like `avatar` or `cover`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_removed`, `not_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "restore"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the uploader (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/media/{file}": {
      "get": {
        "operationId": "getMediaForModeration",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "View an image for moderation",
        "description": "Also works for removed and replaced images that are still kept for a report or an appeal. Not cached.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "description": "`<32 hex characters>.<png|jpg|webp>`, from an image path like `avatar` or `cover`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "The image",
            "content": {
              "image/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`, `file_missing`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/jobs": {
      "get": {
        "operationId": "listJobs",
        "tags": [
          "Jobs"
        ],
        "summary": "Open jobs",
        "description": "Open jobs from accounts, studios and projects, newest first. Counts in `facets` ignore their own filter, so the portal can show them next to each option. Old applications and closed jobs are deleted after the retention periods in ADR 0018.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in title and description",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "role",
            "in": "query",
            "required": false,
            "description": "Role",
            "schema": {
              "type": "string",
              "enum": [
                "scripter",
                "ui_developer",
                "mapper",
                "modeler",
                "animator",
                "texture_artist",
                "vehicle_artist",
                "sound_designer",
                "game_designer",
                "writer",
                "moderator",
                "community_manager",
                "server_admin",
                "tester",
                "translator",
                "video_creator",
                "other"
              ]
            }
          },
          {
            "name": "compensation",
            "in": "query",
            "required": false,
            "description": "Compensation",
            "schema": {
              "type": "string",
              "enum": [
                "volunteer",
                "revenue_share",
                "paid",
                "negotiable"
              ]
            }
          },
          {
            "name": "commitment",
            "in": "query",
            "required": false,
            "description": "Commitment",
            "schema": {
              "type": "string",
              "enum": [
                "one_off",
                "part_time",
                "ongoing"
              ]
            }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "ISO 639-1 code",
            "schema": {
              "type": "string",
              "pattern": "^[a-z]{2}$"
            }
          },
          {
            "name": "skill",
            "in": "query",
            "required": false,
            "description": "Skill",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "studio",
            "in": "query",
            "required": false,
            "description": "Studio slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "project",
            "in": "query",
            "required": false,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "postedBy",
            "in": "query",
            "required": false,
            "description": "Handle of the person who posted",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 to 50 (default 24)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Entries to skip",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "title": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string",
                            "enum": [
                              "scripter",
                              "ui_developer",
                              "mapper",
                              "modeler",
                              "animator",
                              "texture_artist",
                              "vehicle_artist",
                              "sound_designer",
                              "game_designer",
                              "writer",
                              "moderator",
                              "community_manager",
                              "server_admin",
                              "tester",
                              "translator",
                              "video_creator",
                              "other"
                            ]
                          },
                          "description": {
                            "type": "string"
                          },
                          "skills": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "commitment": {
                            "type": "string",
                            "enum": [
                              "one_off",
                              "part_time",
                              "ongoing"
                            ]
                          },
                          "languages": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "links": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "compensation": {
                            "type": "string",
                            "enum": [
                              "volunteer",
                              "revenue_share",
                              "paid",
                              "negotiable"
                            ],
                            "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                          },
                          "compensationNote": {
                            "type": "string",
                            "description": "Free text, for example the amount or the share"
                          },
                          "minAge": {
                            "type": "integer",
                            "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "closed",
                              "expired",
                              "removed"
                            ],
                            "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                          },
                          "postedBy": {
                            "type": "object",
                            "properties": {
                              "handle": {
                                "type": "string",
                                "pattern": "^[a-z0-9_]{3,24}$"
                              },
                              "displayName": {
                                "type": "string"
                              },
                              "avatar": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "description": "Image path under `/v1/media/`"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              }
                            },
                            "required": [
                              "handle",
                              "displayName",
                              "avatar"
                            ]
                          },
                          "studio": {
                            "oneOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "slug": {
                                    "type": "string"
                                  },
                                  "name": {
                                    "type": "string"
                                  },
                                  "logo": {
                                    "oneOf": [
                                      {
                                        "type": "string"
                                      },
                                      {
                                        "type": "null"
                                      }
                                    ]
                                  }
                                },
                                "required": [
                                  "slug",
                                  "name",
                                  "logo"
                                ]
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "project": {
                            "oneOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "slug": {
                                    "type": "string"
                                  },
                                  "title": {
                                    "type": "string"
                                  },
                                  "cover": {
                                    "oneOf": [
                                      {
                                        "type": "string"
                                      },
                                      {
                                        "type": "null"
                                      }
                                    ]
                                  }
                                },
                                "required": [
                                  "slug",
                                  "title",
                                  "cover"
                                ]
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "updatedAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "expiresAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Jobs close 30 days after posting or renewing"
                          },
                          "removed": {
                            "type": "object",
                            "properties": {
                              "at": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "reason": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "at",
                              "reason"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "title",
                          "role",
                          "description",
                          "skills",
                          "commitment",
                          "languages",
                          "links",
                          "compensation",
                          "compensationNote",
                          "minAge",
                          "status",
                          "postedBy",
                          "studio",
                          "project",
                          "createdAt",
                          "updatedAt",
                          "expiresAt"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "facets": {
                      "type": "object",
                      "properties": {
                        "role": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "compensation": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "commitment": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "skills": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "skill": {
                                "type": "string"
                              },
                              "n": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "skill",
                              "n"
                            ]
                          }
                        }
                      },
                      "required": [
                        "role",
                        "compensation",
                        "commitment",
                        "skills"
                      ]
                    },
                    "options": {
                      "type": "object",
                      "properties": {
                        "roles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "compensation": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "commitment": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "limits": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        }
                      },
                      "required": [
                        "roles",
                        "compensation",
                        "commitment",
                        "limits"
                      ]
                    }
                  },
                  "required": [
                    "jobs",
                    "total",
                    "facets",
                    "options"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      },
      "post": {
        "operationId": "postJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Post a job",
        "description": "For yourself, a studio (owner or admin) or a project you can edit (the project decides the studio). You must be 18 or older, have a confirmed email and an account at least 7 days old, and confirm the job posting rules (`acceptRules`): No jobs to rip or convert content from other games, no cheats or account trading, no gambling, no fees for applicants, no requests for passwords or downloads, no discrimination. At most 5 open jobs per account and 10 per studio.\n\nNot available with access tokens.\n\nRate limit: 20 per day per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Posted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "title": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "scripter",
                        "ui_developer",
                        "mapper",
                        "modeler",
                        "animator",
                        "texture_artist",
                        "vehicle_artist",
                        "sound_designer",
                        "game_designer",
                        "writer",
                        "moderator",
                        "community_manager",
                        "server_admin",
                        "tester",
                        "translator",
                        "video_creator",
                        "other"
                      ]
                    },
                    "description": {
                      "type": "string"
                    },
                    "skills": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "commitment": {
                      "type": "string",
                      "enum": [
                        "one_off",
                        "part_time",
                        "ongoing"
                      ]
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "links": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "compensation": {
                      "type": "string",
                      "enum": [
                        "volunteer",
                        "revenue_share",
                        "paid",
                        "negotiable"
                      ],
                      "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                    },
                    "compensationNote": {
                      "type": "string",
                      "description": "Free text, for example the amount or the share"
                    },
                    "minAge": {
                      "type": "integer",
                      "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "closed",
                        "expired",
                        "removed"
                      ],
                      "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                    },
                    "postedBy": {
                      "type": "object",
                      "properties": {
                        "handle": {
                          "type": "string",
                          "pattern": "^[a-z0-9_]{3,24}$"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "avatar": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Image path under `/v1/media/`"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "handle",
                        "displayName",
                        "avatar"
                      ]
                    },
                    "studio": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            },
                            "logo": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "name",
                            "logo"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "project": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "title": {
                              "type": "string"
                            },
                            "cover": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "title",
                            "cover"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "updatedAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Jobs close 30 days after posting or renewing"
                    },
                    "removed": {
                      "type": "object",
                      "properties": {
                        "at": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "reason": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "at",
                        "reason"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "title",
                    "role",
                    "description",
                    "skills",
                    "commitment",
                    "languages",
                    "links",
                    "compensation",
                    "compensationNote",
                    "minAge",
                    "status",
                    "postedBy",
                    "studio",
                    "project",
                    "createdAt",
                    "updatedAt",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `age_restricted`, `email_unverified`, `account_too_new`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `project_not_found`, `studio_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `too_many_open_jobs`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `rules_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per day per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 per day per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 4,
                    "maxLength": 80
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "scripter",
                      "ui_developer",
                      "mapper",
                      "modeler",
                      "animator",
                      "texture_artist",
                      "vehicle_artist",
                      "sound_designer",
                      "game_designer",
                      "writer",
                      "moderator",
                      "community_manager",
                      "server_admin",
                      "tester",
                      "translator",
                      "video_creator",
                      "other"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "minLength": 40,
                    "maxLength": 4000
                  },
                  "commitment": {
                    "type": "string",
                    "enum": [
                      "one_off",
                      "part_time",
                      "ongoing"
                    ]
                  },
                  "compensation": {
                    "type": "string",
                    "enum": [
                      "volunteer",
                      "revenue_share",
                      "paid",
                      "negotiable"
                    ]
                  },
                  "compensationNote": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "skills": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9-]{0,23}$"
                    },
                    "description": "Up to 8 skills, lowercase (`a-z`, `0-9`, `-`)",
                    "maxItems": 8
                  },
                  "languages": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z]{2}$"
                    },
                    "description": "Up to 5 ISO 639-1 language codes, for example `en`",
                    "maxItems": 5
                  },
                  "links": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^https://",
                      "maxLength": 308
                    },
                    "description": "Up to 3 `https://` links",
                    "maxItems": 3
                  },
                  "studio": {
                    "type": "string",
                    "description": "Studio slug",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                  },
                  "project": {
                    "type": "string",
                    "description": "Project slug",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                  },
                  "acceptRules": {
                    "const": true,
                    "description": "Confirms the job posting rules"
                  }
                },
                "required": [
                  "title",
                  "role",
                  "description",
                  "commitment",
                  "compensation",
                  "acceptRules"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/jobs": {
      "get": {
        "operationId": "listMyJobs",
        "tags": [
          "Jobs"
        ],
        "summary": "Jobs you manage",
        "description": "Your own jobs and those of studios you own or administer and projects you own, with application counts per status.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 200, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "title": {
                                "type": "string"
                              },
                              "role": {
                                "type": "string",
                                "enum": [
                                  "scripter",
                                  "ui_developer",
                                  "mapper",
                                  "modeler",
                                  "animator",
                                  "texture_artist",
                                  "vehicle_artist",
                                  "sound_designer",
                                  "game_designer",
                                  "writer",
                                  "moderator",
                                  "community_manager",
                                  "server_admin",
                                  "tester",
                                  "translator",
                                  "video_creator",
                                  "other"
                                ]
                              },
                              "description": {
                                "type": "string"
                              },
                              "skills": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "commitment": {
                                "type": "string",
                                "enum": [
                                  "one_off",
                                  "part_time",
                                  "ongoing"
                                ]
                              },
                              "languages": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "links": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "compensation": {
                                "type": "string",
                                "enum": [
                                  "volunteer",
                                  "revenue_share",
                                  "paid",
                                  "negotiable"
                                ],
                                "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                              },
                              "compensationNote": {
                                "type": "string",
                                "description": "Free text, for example the amount or the share"
                              },
                              "minAge": {
                                "type": "integer",
                                "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "open",
                                  "closed",
                                  "expired",
                                  "removed"
                                ],
                                "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                              },
                              "postedBy": {
                                "type": "object",
                                "properties": {
                                  "handle": {
                                    "type": "string",
                                    "pattern": "^[a-z0-9_]{3,24}$"
                                  },
                                  "displayName": {
                                    "type": "string"
                                  },
                                  "avatar": {
                                    "oneOf": [
                                      {
                                        "type": "string",
                                        "description": "Image path under `/v1/media/`"
                                      },
                                      {
                                        "type": "null"
                                      }
                                    ]
                                  }
                                },
                                "required": [
                                  "handle",
                                  "displayName",
                                  "avatar"
                                ]
                              },
                              "studio": {
                                "oneOf": [
                                  {
                                    "type": "object",
                                    "properties": {
                                      "slug": {
                                        "type": "string"
                                      },
                                      "name": {
                                        "type": "string"
                                      },
                                      "logo": {
                                        "oneOf": [
                                          {
                                            "type": "string"
                                          },
                                          {
                                            "type": "null"
                                          }
                                        ]
                                      }
                                    },
                                    "required": [
                                      "slug",
                                      "name",
                                      "logo"
                                    ]
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "project": {
                                "oneOf": [
                                  {
                                    "type": "object",
                                    "properties": {
                                      "slug": {
                                        "type": "string"
                                      },
                                      "title": {
                                        "type": "string"
                                      },
                                      "cover": {
                                        "oneOf": [
                                          {
                                            "type": "string"
                                          },
                                          {
                                            "type": "null"
                                          }
                                        ]
                                      }
                                    },
                                    "required": [
                                      "slug",
                                      "title",
                                      "cover"
                                    ]
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "createdAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "updatedAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "expiresAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Jobs close 30 days after posting or renewing"
                              },
                              "removed": {
                                "type": "object",
                                "properties": {
                                  "at": {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Unix time in milliseconds"
                                  },
                                  "reason": {
                                    "type": "string"
                                  }
                                },
                                "required": [
                                  "at",
                                  "reason"
                                ]
                              }
                            },
                            "required": [
                              "id",
                              "title",
                              "role",
                              "description",
                              "skills",
                              "commitment",
                              "languages",
                              "links",
                              "compensation",
                              "compensationNote",
                              "minAge",
                              "status",
                              "postedBy",
                              "studio",
                              "project",
                              "createdAt",
                              "updatedAt",
                              "expiresAt"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "applications": {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "integer"
                                }
                              }
                            },
                            "required": [
                              "applications"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "jobs"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "operationId": "getJob",
        "tags": [
          "Jobs"
        ],
        "summary": "A job",
        "description": "Closed and expired jobs stay readable. Removed jobs and jobs of hidden studios or projects are only visible to the team and moderators. With login, `viewer` says whether you can apply and why not (`own_job`, `not_open`, `already_applied`, `not_allowed`, `age_16`, `age_18`).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "title": {
                          "type": "string"
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "scripter",
                            "ui_developer",
                            "mapper",
                            "modeler",
                            "animator",
                            "texture_artist",
                            "vehicle_artist",
                            "sound_designer",
                            "game_designer",
                            "writer",
                            "moderator",
                            "community_manager",
                            "server_admin",
                            "tester",
                            "translator",
                            "video_creator",
                            "other"
                          ]
                        },
                        "description": {
                          "type": "string"
                        },
                        "skills": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "commitment": {
                          "type": "string",
                          "enum": [
                            "one_off",
                            "part_time",
                            "ongoing"
                          ]
                        },
                        "languages": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "links": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "compensation": {
                          "type": "string",
                          "enum": [
                            "volunteer",
                            "revenue_share",
                            "paid",
                            "negotiable"
                          ],
                          "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                        },
                        "compensationNote": {
                          "type": "string",
                          "description": "Free text, for example the amount or the share"
                        },
                        "minAge": {
                          "type": "integer",
                          "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "open",
                            "closed",
                            "expired",
                            "removed"
                          ],
                          "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                        },
                        "postedBy": {
                          "type": "object",
                          "properties": {
                            "handle": {
                              "type": "string",
                              "pattern": "^[a-z0-9_]{3,24}$"
                            },
                            "displayName": {
                              "type": "string"
                            },
                            "avatar": {
                              "oneOf": [
                                {
                                  "type": "string",
                                  "description": "Image path under `/v1/media/`"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "handle",
                            "displayName",
                            "avatar"
                          ]
                        },
                        "studio": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "slug": {
                                  "type": "string"
                                },
                                "name": {
                                  "type": "string"
                                },
                                "logo": {
                                  "oneOf": [
                                    {
                                      "type": "string"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "slug",
                                "name",
                                "logo"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "project": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "slug": {
                                  "type": "string"
                                },
                                "title": {
                                  "type": "string"
                                },
                                "cover": {
                                  "oneOf": [
                                    {
                                      "type": "string"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "slug",
                                "title",
                                "cover"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Jobs close 30 days after posting or renewing"
                        },
                        "removed": {
                          "type": "object",
                          "properties": {
                            "at": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            "reason": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "at",
                            "reason"
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "title",
                        "role",
                        "description",
                        "skills",
                        "commitment",
                        "languages",
                        "links",
                        "compensation",
                        "compensationNote",
                        "minAge",
                        "status",
                        "postedBy",
                        "studio",
                        "project",
                        "createdAt",
                        "updatedAt",
                        "expiresAt"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "viewer": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "canManage": {
                                  "type": "boolean"
                                },
                                "canApply": {
                                  "type": "boolean"
                                },
                                "applyBlockedReason": {
                                  "oneOf": [
                                    {
                                      "type": "string"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                },
                                "application": {
                                  "oneOf": [
                                    {
                                      "type": "object",
                                      "properties": {
                                        "id": {
                                          "type": "integer"
                                        },
                                        "status": {
                                          "type": "string",
                                          "enum": [
                                            "new",
                                            "shortlisted",
                                            "accepted",
                                            "declined",
                                            "withdrawn"
                                          ]
                                        }
                                      },
                                      "required": [
                                        "id",
                                        "status"
                                      ]
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                },
                                "applications": {
                                  "type": "integer",
                                  "description": "Only for the team: applications that are not withdrawn"
                                }
                              },
                              "required": [
                                "canManage",
                                "canApply",
                                "applyBlockedReason",
                                "application"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "viewer"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      },
      "patch": {
        "operationId": "updateJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Edit a job",
        "description": "Only for the person who posted it, studio owners and admins, and people who can edit the project. A `volunteer` job cannot change its compensation while people under 18 have open applications.\n\nNot available with access tokens.\n\nRate limit: 120 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "title": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "scripter",
                        "ui_developer",
                        "mapper",
                        "modeler",
                        "animator",
                        "texture_artist",
                        "vehicle_artist",
                        "sound_designer",
                        "game_designer",
                        "writer",
                        "moderator",
                        "community_manager",
                        "server_admin",
                        "tester",
                        "translator",
                        "video_creator",
                        "other"
                      ]
                    },
                    "description": {
                      "type": "string"
                    },
                    "skills": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "commitment": {
                      "type": "string",
                      "enum": [
                        "one_off",
                        "part_time",
                        "ongoing"
                      ]
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "links": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "compensation": {
                      "type": "string",
                      "enum": [
                        "volunteer",
                        "revenue_share",
                        "paid",
                        "negotiable"
                      ],
                      "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                    },
                    "compensationNote": {
                      "type": "string",
                      "description": "Free text, for example the amount or the share"
                    },
                    "minAge": {
                      "type": "integer",
                      "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "closed",
                        "expired",
                        "removed"
                      ],
                      "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                    },
                    "postedBy": {
                      "type": "object",
                      "properties": {
                        "handle": {
                          "type": "string",
                          "pattern": "^[a-z0-9_]{3,24}$"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "avatar": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Image path under `/v1/media/`"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "handle",
                        "displayName",
                        "avatar"
                      ]
                    },
                    "studio": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            },
                            "logo": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "name",
                            "logo"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "project": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "title": {
                              "type": "string"
                            },
                            "cover": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "title",
                            "cover"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "updatedAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Jobs close 30 days after posting or renewing"
                    },
                    "removed": {
                      "type": "object",
                      "properties": {
                        "at": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "reason": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "at",
                        "reason"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "title",
                    "role",
                    "description",
                    "skills",
                    "commitment",
                    "languages",
                    "links",
                    "compensation",
                    "compensationNote",
                    "minAge",
                    "status",
                    "postedBy",
                    "studio",
                    "project",
                    "createdAt",
                    "updatedAt",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `removed`, `minor_applicants`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 4,
                    "maxLength": 80
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "scripter",
                      "ui_developer",
                      "mapper",
                      "modeler",
                      "animator",
                      "texture_artist",
                      "vehicle_artist",
                      "sound_designer",
                      "game_designer",
                      "writer",
                      "moderator",
                      "community_manager",
                      "server_admin",
                      "tester",
                      "translator",
                      "video_creator",
                      "other"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "minLength": 40,
                    "maxLength": 4000
                  },
                  "commitment": {
                    "type": "string",
                    "enum": [
                      "one_off",
                      "part_time",
                      "ongoing"
                    ]
                  },
                  "compensation": {
                    "type": "string",
                    "enum": [
                      "volunteer",
                      "revenue_share",
                      "paid",
                      "negotiable"
                    ]
                  },
                  "compensationNote": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "skills": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9-]{0,23}$"
                    },
                    "description": "Up to 8 skills, lowercase (`a-z`, `0-9`, `-`)",
                    "maxItems": 8
                  },
                  "languages": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z]{2}$"
                    },
                    "description": "Up to 5 ISO 639-1 language codes, for example `en`",
                    "maxItems": 5
                  },
                  "links": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^https://",
                      "maxLength": 308
                    },
                    "description": "Up to 3 `https://` links",
                    "maxItems": 3
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Delete a job",
        "description": "Deletes the job with all applications and conversations. Reported versions stay as copies in the reports.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/jobs/{id}/close": {
      "post": {
        "operationId": "closeJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Close a job",
        "description": "No new applications. Existing conversations continue.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Closed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "title": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "scripter",
                        "ui_developer",
                        "mapper",
                        "modeler",
                        "animator",
                        "texture_artist",
                        "vehicle_artist",
                        "sound_designer",
                        "game_designer",
                        "writer",
                        "moderator",
                        "community_manager",
                        "server_admin",
                        "tester",
                        "translator",
                        "video_creator",
                        "other"
                      ]
                    },
                    "description": {
                      "type": "string"
                    },
                    "skills": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "commitment": {
                      "type": "string",
                      "enum": [
                        "one_off",
                        "part_time",
                        "ongoing"
                      ]
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "links": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "compensation": {
                      "type": "string",
                      "enum": [
                        "volunteer",
                        "revenue_share",
                        "paid",
                        "negotiable"
                      ],
                      "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                    },
                    "compensationNote": {
                      "type": "string",
                      "description": "Free text, for example the amount or the share"
                    },
                    "minAge": {
                      "type": "integer",
                      "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "closed",
                        "expired",
                        "removed"
                      ],
                      "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                    },
                    "postedBy": {
                      "type": "object",
                      "properties": {
                        "handle": {
                          "type": "string",
                          "pattern": "^[a-z0-9_]{3,24}$"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "avatar": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Image path under `/v1/media/`"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "handle",
                        "displayName",
                        "avatar"
                      ]
                    },
                    "studio": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            },
                            "logo": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "name",
                            "logo"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "project": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "title": {
                              "type": "string"
                            },
                            "cover": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "title",
                            "cover"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "updatedAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Jobs close 30 days after posting or renewing"
                    },
                    "removed": {
                      "type": "object",
                      "properties": {
                        "at": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "reason": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "at",
                        "reason"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "title",
                    "role",
                    "description",
                    "skills",
                    "commitment",
                    "languages",
                    "links",
                    "compensation",
                    "compensationNote",
                    "minAge",
                    "status",
                    "postedBy",
                    "studio",
                    "project",
                    "createdAt",
                    "updatedAt",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/jobs/{id}/renew": {
      "post": {
        "operationId": "renewJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Reopen or extend a job",
        "description": "Open for another 30 days from now. Counts against the open-job limits.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Open",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "title": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "scripter",
                        "ui_developer",
                        "mapper",
                        "modeler",
                        "animator",
                        "texture_artist",
                        "vehicle_artist",
                        "sound_designer",
                        "game_designer",
                        "writer",
                        "moderator",
                        "community_manager",
                        "server_admin",
                        "tester",
                        "translator",
                        "video_creator",
                        "other"
                      ]
                    },
                    "description": {
                      "type": "string"
                    },
                    "skills": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "commitment": {
                      "type": "string",
                      "enum": [
                        "one_off",
                        "part_time",
                        "ongoing"
                      ]
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "links": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "compensation": {
                      "type": "string",
                      "enum": [
                        "volunteer",
                        "revenue_share",
                        "paid",
                        "negotiable"
                      ],
                      "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                    },
                    "compensationNote": {
                      "type": "string",
                      "description": "Free text, for example the amount or the share"
                    },
                    "minAge": {
                      "type": "integer",
                      "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "closed",
                        "expired",
                        "removed"
                      ],
                      "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                    },
                    "postedBy": {
                      "type": "object",
                      "properties": {
                        "handle": {
                          "type": "string",
                          "pattern": "^[a-z0-9_]{3,24}$"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "avatar": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Image path under `/v1/media/`"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "handle",
                        "displayName",
                        "avatar"
                      ]
                    },
                    "studio": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            },
                            "logo": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "name",
                            "logo"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "project": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "slug": {
                              "type": "string"
                            },
                            "title": {
                              "type": "string"
                            },
                            "cover": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "slug",
                            "title",
                            "cover"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "updatedAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Jobs close 30 days after posting or renewing"
                    },
                    "removed": {
                      "type": "object",
                      "properties": {
                        "at": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "reason": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "at",
                        "reason"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "title",
                    "role",
                    "description",
                    "skills",
                    "commitment",
                    "languages",
                    "links",
                    "compensation",
                    "compensationNote",
                    "minAge",
                    "status",
                    "postedBy",
                    "studio",
                    "project",
                    "createdAt",
                    "updatedAt",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `removed`, `page_hidden`, `too_many_open_jobs`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/jobs/{id}/applications": {
      "post": {
        "operationId": "applyForJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Apply for a job",
        "description": "One application per job. From 16 for `volunteer` jobs, from 18 for everything else; accounts without a birth month count as under 16. The team gets a `job_application` notification (at most one per job every 30 minutes).\n\nNot available with access tokens.\n\nRate limit: 20 per day per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Applied",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "jobId": {
                      "type": "integer"
                    },
                    "status": {
                      "const": "new"
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "id",
                    "jobId",
                    "status",
                    "createdAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `age_restricted`, `not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `own_job`, `not_open`, `already_applied`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per day per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 per day per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 2000
                  },
                  "links": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^https://",
                      "maxLength": 308
                    },
                    "description": "Up to 3 `https://` links",
                    "maxItems": 3
                  }
                },
                "required": [
                  "message"
                ]
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listJobApplications",
        "tags": [
          "Jobs"
        ],
        "summary": "Applications for a job",
        "description": "Only for the team. Withdrawn applications are not listed.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Applications, oldest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "title": {
                          "type": "string"
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "scripter",
                            "ui_developer",
                            "mapper",
                            "modeler",
                            "animator",
                            "texture_artist",
                            "vehicle_artist",
                            "sound_designer",
                            "game_designer",
                            "writer",
                            "moderator",
                            "community_manager",
                            "server_admin",
                            "tester",
                            "translator",
                            "video_creator",
                            "other"
                          ]
                        },
                        "description": {
                          "type": "string"
                        },
                        "skills": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "commitment": {
                          "type": "string",
                          "enum": [
                            "one_off",
                            "part_time",
                            "ongoing"
                          ]
                        },
                        "languages": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "links": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "compensation": {
                          "type": "string",
                          "enum": [
                            "volunteer",
                            "revenue_share",
                            "paid",
                            "negotiable"
                          ],
                          "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                        },
                        "compensationNote": {
                          "type": "string",
                          "description": "Free text, for example the amount or the share"
                        },
                        "minAge": {
                          "type": "integer",
                          "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "open",
                            "closed",
                            "expired",
                            "removed"
                          ],
                          "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                        },
                        "postedBy": {
                          "type": "object",
                          "properties": {
                            "handle": {
                              "type": "string",
                              "pattern": "^[a-z0-9_]{3,24}$"
                            },
                            "displayName": {
                              "type": "string"
                            },
                            "avatar": {
                              "oneOf": [
                                {
                                  "type": "string",
                                  "description": "Image path under `/v1/media/`"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "handle",
                            "displayName",
                            "avatar"
                          ]
                        },
                        "studio": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "slug": {
                                  "type": "string"
                                },
                                "name": {
                                  "type": "string"
                                },
                                "logo": {
                                  "oneOf": [
                                    {
                                      "type": "string"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "slug",
                                "name",
                                "logo"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "project": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "slug": {
                                  "type": "string"
                                },
                                "title": {
                                  "type": "string"
                                },
                                "cover": {
                                  "oneOf": [
                                    {
                                      "type": "string"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "slug",
                                "title",
                                "cover"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "expiresAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Jobs close 30 days after posting or renewing"
                        },
                        "removed": {
                          "type": "object",
                          "properties": {
                            "at": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            "reason": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "at",
                            "reason"
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "title",
                        "role",
                        "description",
                        "skills",
                        "commitment",
                        "languages",
                        "links",
                        "compensation",
                        "compensationNote",
                        "minAge",
                        "status",
                        "postedBy",
                        "studio",
                        "project",
                        "createdAt",
                        "updatedAt",
                        "expiresAt"
                      ]
                    },
                    "applications": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "message": {
                            "type": "string"
                          },
                          "links": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "new",
                              "shortlisted",
                              "accepted",
                              "declined",
                              "withdrawn"
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "updatedAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "messages": {
                            "type": "integer"
                          },
                          "lastMessageAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "applicant": {
                            "type": "object",
                            "properties": {
                              "handle": {
                                "type": "string",
                                "pattern": "^[a-z0-9_]{3,24}$"
                              },
                              "displayName": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "handle",
                              "displayName"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "message",
                          "links",
                          "status",
                          "createdAt",
                          "updatedAt",
                          "messages",
                          "lastMessageAt",
                          "applicant"
                        ]
                      }
                    }
                  },
                  "required": [
                    "job",
                    "applications"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/me/applications": {
      "get": {
        "operationId": "listMyApplications",
        "tags": [
          "Jobs"
        ],
        "summary": "Your applications",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 200, most recent activity first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applications": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "new",
                              "shortlisted",
                              "accepted",
                              "declined",
                              "withdrawn"
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "updatedAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "jobId": {
                            "type": "integer"
                          },
                          "lastMessageAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "job": {
                            "oneOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "integer"
                                  },
                                  "title": {
                                    "type": "string"
                                  },
                                  "role": {
                                    "type": "string",
                                    "enum": [
                                      "scripter",
                                      "ui_developer",
                                      "mapper",
                                      "modeler",
                                      "animator",
                                      "texture_artist",
                                      "vehicle_artist",
                                      "sound_designer",
                                      "game_designer",
                                      "writer",
                                      "moderator",
                                      "community_manager",
                                      "server_admin",
                                      "tester",
                                      "translator",
                                      "video_creator",
                                      "other"
                                    ]
                                  },
                                  "description": {
                                    "type": "string"
                                  },
                                  "skills": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  },
                                  "commitment": {
                                    "type": "string",
                                    "enum": [
                                      "one_off",
                                      "part_time",
                                      "ongoing"
                                    ]
                                  },
                                  "languages": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  },
                                  "links": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  },
                                  "compensation": {
                                    "type": "string",
                                    "enum": [
                                      "volunteer",
                                      "revenue_share",
                                      "paid",
                                      "negotiable"
                                    ],
                                    "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                                  },
                                  "compensationNote": {
                                    "type": "string",
                                    "description": "Free text, for example the amount or the share"
                                  },
                                  "minAge": {
                                    "type": "integer",
                                    "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                                  },
                                  "status": {
                                    "type": "string",
                                    "enum": [
                                      "open",
                                      "closed",
                                      "expired",
                                      "removed"
                                    ],
                                    "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                                  },
                                  "postedBy": {
                                    "type": "object",
                                    "properties": {
                                      "handle": {
                                        "type": "string",
                                        "pattern": "^[a-z0-9_]{3,24}$"
                                      },
                                      "displayName": {
                                        "type": "string"
                                      },
                                      "avatar": {
                                        "oneOf": [
                                          {
                                            "type": "string",
                                            "description": "Image path under `/v1/media/`"
                                          },
                                          {
                                            "type": "null"
                                          }
                                        ]
                                      }
                                    },
                                    "required": [
                                      "handle",
                                      "displayName",
                                      "avatar"
                                    ]
                                  },
                                  "studio": {
                                    "oneOf": [
                                      {
                                        "type": "object",
                                        "properties": {
                                          "slug": {
                                            "type": "string"
                                          },
                                          "name": {
                                            "type": "string"
                                          },
                                          "logo": {
                                            "oneOf": [
                                              {
                                                "type": "string"
                                              },
                                              {
                                                "type": "null"
                                              }
                                            ]
                                          }
                                        },
                                        "required": [
                                          "slug",
                                          "name",
                                          "logo"
                                        ]
                                      },
                                      {
                                        "type": "null"
                                      }
                                    ]
                                  },
                                  "project": {
                                    "oneOf": [
                                      {
                                        "type": "object",
                                        "properties": {
                                          "slug": {
                                            "type": "string"
                                          },
                                          "title": {
                                            "type": "string"
                                          },
                                          "cover": {
                                            "oneOf": [
                                              {
                                                "type": "string"
                                              },
                                              {
                                                "type": "null"
                                              }
                                            ]
                                          }
                                        },
                                        "required": [
                                          "slug",
                                          "title",
                                          "cover"
                                        ]
                                      },
                                      {
                                        "type": "null"
                                      }
                                    ]
                                  },
                                  "createdAt": {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Unix time in milliseconds"
                                  },
                                  "updatedAt": {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Unix time in milliseconds"
                                  },
                                  "expiresAt": {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Jobs close 30 days after posting or renewing"
                                  },
                                  "removed": {
                                    "type": "object",
                                    "properties": {
                                      "at": {
                                        "type": "integer",
                                        "format": "int64",
                                        "description": "Unix time in milliseconds"
                                      },
                                      "reason": {
                                        "type": "string"
                                      }
                                    },
                                    "required": [
                                      "at",
                                      "reason"
                                    ]
                                  }
                                },
                                "required": [
                                  "id",
                                  "title",
                                  "role",
                                  "description",
                                  "skills",
                                  "commitment",
                                  "languages",
                                  "links",
                                  "compensation",
                                  "compensationNote",
                                  "minAge",
                                  "status",
                                  "postedBy",
                                  "studio",
                                  "project",
                                  "createdAt",
                                  "updatedAt",
                                  "expiresAt"
                                ]
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "integer"
                                  },
                                  "title": {
                                    "type": "string"
                                  },
                                  "status": {
                                    "const": "removed"
                                  }
                                },
                                "required": [
                                  "id",
                                  "title",
                                  "status"
                                ]
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "status",
                          "createdAt",
                          "updatedAt",
                          "jobId",
                          "lastMessageAt",
                          "job"
                        ]
                      }
                    }
                  },
                  "required": [
                    "applications"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/applications/{id}": {
      "get": {
        "operationId": "getApplication",
        "tags": [
          "Jobs"
        ],
        "summary": "An application with its conversation",
        "description": "Only for the applicant and the team. Messages are not scanned; either side can report a message. `safetyNotice` is true for viewers under 18 (or without a birth month), the portal then shows a fixed safety notice.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Application",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "new",
                        "shortlisted",
                        "accepted",
                        "declined",
                        "withdrawn"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "links": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "updatedAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "applicant": {
                      "type": "object",
                      "properties": {
                        "handle": {
                          "type": "string",
                          "pattern": "^[a-z0-9_]{3,24}$"
                        },
                        "displayName": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "handle",
                        "displayName"
                      ]
                    },
                    "job": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "integer"
                            },
                            "title": {
                              "type": "string"
                            },
                            "role": {
                              "type": "string",
                              "enum": [
                                "scripter",
                                "ui_developer",
                                "mapper",
                                "modeler",
                                "animator",
                                "texture_artist",
                                "vehicle_artist",
                                "sound_designer",
                                "game_designer",
                                "writer",
                                "moderator",
                                "community_manager",
                                "server_admin",
                                "tester",
                                "translator",
                                "video_creator",
                                "other"
                              ]
                            },
                            "description": {
                              "type": "string"
                            },
                            "skills": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "commitment": {
                              "type": "string",
                              "enum": [
                                "one_off",
                                "part_time",
                                "ongoing"
                              ]
                            },
                            "languages": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "links": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "compensation": {
                              "type": "string",
                              "enum": [
                                "volunteer",
                                "revenue_share",
                                "paid",
                                "negotiable"
                              ],
                              "description": "What is offered. Only a statement: no money or GAMPEONS move through GAMPEON. `paid` is arranged in euros between the parties"
                            },
                            "compensationNote": {
                              "type": "string",
                              "description": "Free text, for example the amount or the share"
                            },
                            "minAge": {
                              "type": "integer",
                              "description": "Minimum age to apply: 16 for `volunteer`, 18 for everything else"
                            },
                            "status": {
                              "type": "string",
                              "enum": [
                                "open",
                                "closed",
                                "expired",
                                "removed"
                              ],
                              "description": "`expired`: open, but past `expiresAt`. `removed`: removed by moderation, only visible to the team and moderators"
                            },
                            "postedBy": {
                              "type": "object",
                              "properties": {
                                "handle": {
                                  "type": "string",
                                  "pattern": "^[a-z0-9_]{3,24}$"
                                },
                                "displayName": {
                                  "type": "string"
                                },
                                "avatar": {
                                  "oneOf": [
                                    {
                                      "type": "string",
                                      "description": "Image path under `/v1/media/`"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "handle",
                                "displayName",
                                "avatar"
                              ]
                            },
                            "studio": {
                              "oneOf": [
                                {
                                  "type": "object",
                                  "properties": {
                                    "slug": {
                                      "type": "string"
                                    },
                                    "name": {
                                      "type": "string"
                                    },
                                    "logo": {
                                      "oneOf": [
                                        {
                                          "type": "string"
                                        },
                                        {
                                          "type": "null"
                                        }
                                      ]
                                    }
                                  },
                                  "required": [
                                    "slug",
                                    "name",
                                    "logo"
                                  ]
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "project": {
                              "oneOf": [
                                {
                                  "type": "object",
                                  "properties": {
                                    "slug": {
                                      "type": "string"
                                    },
                                    "title": {
                                      "type": "string"
                                    },
                                    "cover": {
                                      "oneOf": [
                                        {
                                          "type": "string"
                                        },
                                        {
                                          "type": "null"
                                        }
                                      ]
                                    }
                                  },
                                  "required": [
                                    "slug",
                                    "title",
                                    "cover"
                                  ]
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "createdAt": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            "updatedAt": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            "expiresAt": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Jobs close 30 days after posting or renewing"
                            },
                            "removed": {
                              "type": "object",
                              "properties": {
                                "at": {
                                  "type": "integer",
                                  "format": "int64",
                                  "description": "Unix time in milliseconds"
                                },
                                "reason": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "at",
                                "reason"
                              ]
                            }
                          },
                          "required": [
                            "id",
                            "title",
                            "role",
                            "description",
                            "skills",
                            "commitment",
                            "languages",
                            "links",
                            "compensation",
                            "compensationNote",
                            "minAge",
                            "status",
                            "postedBy",
                            "studio",
                            "project",
                            "createdAt",
                            "updatedAt",
                            "expiresAt"
                          ]
                        },
                        {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "integer"
                            },
                            "title": {
                              "type": "string"
                            },
                            "status": {
                              "const": "removed"
                            }
                          },
                          "required": [
                            "id",
                            "title",
                            "status"
                          ]
                        }
                      ]
                    },
                    "messages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "body": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "`null` if removed by moderation"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "removed": {
                            "type": "boolean"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "author": {
                            "oneOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "handle": {
                                    "type": "string",
                                    "pattern": "^[a-z0-9_]{3,24}$"
                                  },
                                  "displayName": {
                                    "type": "string"
                                  }
                                },
                                "required": [
                                  "handle",
                                  "displayName"
                                ]
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "mine": {
                            "type": "boolean"
                          },
                          "side": {
                            "type": "string",
                            "enum": [
                              "applicant",
                              "team"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "body",
                          "removed",
                          "createdAt",
                          "author",
                          "mine",
                          "side"
                        ]
                      }
                    },
                    "viewer": {
                      "type": "object",
                      "properties": {
                        "side": {
                          "type": "string",
                          "enum": [
                            "applicant",
                            "manager"
                          ]
                        },
                        "canMessage": {
                          "type": "boolean"
                        },
                        "safetyNotice": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "side",
                        "canMessage",
                        "safetyNotice"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "status",
                    "message",
                    "links",
                    "createdAt",
                    "updatedAt",
                    "applicant",
                    "job",
                    "messages",
                    "viewer"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "patch": {
        "operationId": "updateApplication",
        "tags": [
          "Jobs"
        ],
        "summary": "Change the status of an application",
        "description": "The team sets `shortlisted`, `accepted` or `declined` (the applicant gets an `application_status` notification). The applicant can only set `withdrawn`. Withdrawn applications are deleted after 30 days.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "new",
                        "shortlisted",
                        "accepted",
                        "declined",
                        "withdrawn"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `withdrawn`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "new",
                      "shortlisted",
                      "accepted",
                      "declined",
                      "withdrawn"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/applications/{id}/messages": {
      "post": {
        "operationId": "sendApplicationMessage",
        "tags": [
          "Jobs"
        ],
        "summary": "Send a message about an application",
        "description": "Text only, no attachments. Closed for withdrawn and declined applications and removed jobs. The other side gets an `application_message` notification (at most one every 30 minutes).\n\nNot available with access tokens.\n\nRate limit: 50 per day per account and conversation.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "body": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "id",
                    "body",
                    "createdAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `conversation_closed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 50 per day per account and conversation. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "50 per day per account and conversation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/legal/job-report-categories": {
      "get": {
        "operationId": "getJobReportCategories",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report categories for jobs and messages",
        "security": [],
        "responses": {
          "200": {
            "description": "Categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "scam",
                          "illegal_work",
                          "child_safety",
                          "harassment",
                          "hate_speech",
                          "spam",
                          "copyright",
                          "other"
                        ]
                      }
                    },
                    "anonymous": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Categories that need no name and email"
                    }
                  },
                  "required": [
                    "categories",
                    "anonymous"
                  ]
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none"
      }
    },
    "/v1/jobs/{id}/reports": {
      "post": {
        "operationId": "reportJob",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report a job",
        "description": "Notice under Art. 16 DSA, also without an account. Name and email are required except for `child_safety`. A copy of the job is kept with the report.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you already reported it)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "scam",
                      "illegal_work",
                      "child_safety",
                      "harassment",
                      "hate_speech",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/job-messages/{id}/reports": {
      "post": {
        "operationId": "reportJobMessage",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report a message in an application conversation",
        "description": "Only people in the conversation can report, never their own message. A copy of the message is kept with the report.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Message ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you already reported it)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `own_message`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "scam",
                      "illegal_work",
                      "child_safety",
                      "harassment",
                      "hate_speech",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/job-reports": {
      "get": {
        "operationId": "listJobReports",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Reports about jobs and messages",
        "description": "Up to 200, `urgent` ones (`child_safety`) first, then oldest first.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default: open",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "actioned",
                "rejected"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reports",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "targetType": {
                            "type": "string",
                            "enum": [
                              "job",
                              "message"
                            ]
                          },
                          "jobId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "messageId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "snapshot": {
                            "type": "string",
                            "description": "Reported text"
                          },
                          "category": {
                            "type": "string"
                          },
                          "explanation": {
                            "type": "string"
                          },
                          "reporterName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "authorHandle": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "authorName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "removedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "deleted": {
                            "type": "boolean",
                            "description": "Deleted in the meantime"
                          },
                          "urgent": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "targetType",
                          "jobId",
                          "messageId",
                          "snapshot",
                          "category",
                          "explanation",
                          "reporterName",
                          "status",
                          "createdAt",
                          "authorHandle",
                          "authorName",
                          "removedAt",
                          "deleted",
                          "urgent"
                        ]
                      }
                    }
                  },
                  "required": [
                    "reports"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/job-reports/{id}/decision": {
      "post": {
        "operationId": "decideJobReport",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide on a report about a job or message",
        "description": "`remove` removes the job or message, closes all open reports about it and informs the author (Art. 17 DSA) and the reporting people (Art. 16(5)). `reject` keeps it and informs the reporting person.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Report ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "reject"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/jobs/{id}/decision": {
      "post": {
        "operationId": "moderateJob",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Remove or restore a job without a report",
        "description": "Always with a statement of reasons, which the author sees.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_removed`, `not_removed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "restore"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/job-appeals": {
      "post": {
        "operationId": "appealJobRemoval",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Appeal the removal of your job or message",
        "description": "One appeal per decision (Art. 20 DSA). Someone from moderation decides and explains the outcome.\n\nNot available with access tokens.\n\nRate limit: 10 per day per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Appeal received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appealId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "appealId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `appeal_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per day per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per day per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decisionId": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "decisionId",
                  "text"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/me/job-decisions": {
      "get": {
        "operationId": "listMyJobDecisions",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Removals of your jobs and messages",
        "description": "Not available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 100, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "decisionId": {
                            "type": "string"
                          },
                          "targetType": {
                            "type": "string",
                            "enum": [
                              "job",
                              "message"
                            ]
                          },
                          "jobId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "snapshot": {
                            "type": "string"
                          },
                          "statementOfReasons": {
                            "type": "string"
                          },
                          "legalGround": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "appealId": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "appealStatus": {
                            "oneOf": [
                              {
                                "type": "string",
                                "enum": [
                                  "open",
                                  "upheld",
                                  "reversed"
                                ]
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "outcomeReasons": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "decisionId",
                          "targetType",
                          "jobId",
                          "snapshot",
                          "statementOfReasons",
                          "legalGround",
                          "createdAt",
                          "appealId",
                          "appealStatus",
                          "outcomeReasons",
                          "decidedAt"
                        ]
                      }
                    }
                  },
                  "required": [
                    "decisions"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/moderation/job-appeals": {
      "get": {
        "operationId": "listJobAppeals",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Appeals about jobs and messages",
        "description": "`ownDecision` shows whether you made the removal yourself, so someone else can review it.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default: open",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "upheld",
                "reversed"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Appeals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "text": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "decidedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "outcomeReasons": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "decisionId": {
                            "type": "string"
                          },
                          "targetType": {
                            "type": "string",
                            "enum": [
                              "job",
                              "message"
                            ]
                          },
                          "jobId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "snapshot": {
                            "type": "string"
                          },
                          "statementOfReasons": {
                            "type": "string"
                          },
                          "legalGround": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "removedAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "authorHandle": {
                            "type": "string"
                          },
                          "authorName": {
                            "type": "string"
                          },
                          "ownDecision": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "text",
                          "status",
                          "createdAt",
                          "decidedAt",
                          "outcomeReasons",
                          "decisionId",
                          "targetType",
                          "jobId",
                          "snapshot",
                          "statementOfReasons",
                          "legalGround",
                          "removedAt",
                          "authorHandle",
                          "authorName",
                          "ownDecision"
                        ]
                      }
                    }
                  },
                  "required": [
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/job-appeals/{id}/decision": {
      "post": {
        "operationId": "decideJobAppeal",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide on an appeal about a job or message",
        "description": "`reverse` restores the job or message unless another removal still applies. The author gets the reasons and the notice about out-of-court dispute settlement (Art. 21 DSA).\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Appeal ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "appealId": {
                      "type": "string"
                    },
                    "outcome": {
                      "type": "string"
                    },
                    "restored": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "appealId",
                    "outcome",
                    "restored"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "uphold",
                      "reverse"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the author (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  }
                },
                "required": [
                  "outcome",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/job-stats": {
      "get": {
        "operationId": "getJobModerationStats",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Numbers for the transparency report (jobs)",
        "description": "Requires the role moderator or admin.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "targetType": {
                            "type": "string"
                          },
                          "category": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "targetType",
                          "category",
                          "status",
                          "n"
                        ]
                      }
                    },
                    "decisions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "targetType": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "onNotice": {
                            "type": "integer"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "targetType",
                          "action",
                          "onNotice",
                          "n"
                        ]
                      }
                    },
                    "appeals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "status": {
                            "type": "string"
                          },
                          "n": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "status",
                          "n"
                        ]
                      }
                    }
                  },
                  "required": [
                    "reports",
                    "decisions",
                    "appeals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/projects/{slug}/views": {
      "post": {
        "operationId": "countProjectView",
        "tags": [
          "Projects"
        ],
        "summary": "Count a page view",
        "description": "The portal calls this once when the project page opens. Counted once per visitor and day; the team and drafts are not counted. Nothing about the visitor is stored, only a counter per project and day.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Received (counted or not)"
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      }
    },
    "/v1/projects/{slug}/favorite": {
      "put": {
        "operationId": "favoriteProject",
        "tags": [
          "Projects"
        ],
        "summary": "Add a project to your favorites",
        "description": "Published projects only. Calling it again changes nothing.\n\nNot available with access tokens.\n\nRate limit: 120 per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Favorite",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "favorited": {
                      "type": "boolean"
                    },
                    "favorites": {
                      "type": "integer",
                      "description": "Favorites of the project now"
                    }
                  },
                  "required": [
                    "favorited",
                    "favorites"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 per hour per account"
      },
      "delete": {
        "operationId": "unfavoriteProject",
        "tags": [
          "Projects"
        ],
        "summary": "Remove a project from your favorites",
        "description": "Not available with access tokens.\n\nRate limit: 120 per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Not a favorite any more",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "favorited": {
                      "type": "boolean"
                    },
                    "favorites": {
                      "type": "integer",
                      "description": "Favorites of the project now"
                    }
                  },
                  "required": [
                    "favorited",
                    "favorites"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 per hour per account"
      }
    },
    "/v1/projects/{slug}/stats": {
      "get": {
        "operationId": "getProjectStats",
        "tags": [
          "Projects"
        ],
        "summary": "Views and favorites per day",
        "description": "For the team: the owner and all members of the studio. Days in UTC, oldest first, days without activity included.\n\nAccess tokens: scope `projects:read`.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "1 to 90 (default 30)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 30
            }
          }
        ],
        "security": [
          {
            "session": []
          },
          {
            "accessToken": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Statistics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "day": {
                            "type": "string",
                            "description": "YYYY-MM-DD"
                          },
                          "views": {
                            "type": "integer"
                          },
                          "favorites": {
                            "type": "integer",
                            "description": "Favorites added that day"
                          }
                        },
                        "required": [
                          "day",
                          "views",
                          "favorites"
                        ]
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "views": {
                          "type": "integer"
                        },
                        "favorites": {
                          "type": "integer"
                        },
                        "viewsInRange": {
                          "type": "integer"
                        },
                        "favoritesInRange": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "views",
                        "favorites",
                        "viewsInRange",
                        "favoritesInRange"
                      ]
                    },
                    "storage": {
                      "type": "object",
                      "properties": {
                        "files": {
                          "type": "integer",
                          "description": "Images of the project: key art and gallery"
                        },
                        "bytes": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "files",
                        "bytes"
                      ]
                    },
                    "gallery": {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "items",
                        "max"
                      ]
                    }
                  },
                  "required": [
                    "days",
                    "totals",
                    "storage",
                    "gallery"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `insufficient_scope`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-token-scope": "projects:read"
      }
    },
    "/v1/projects/{slug}/gallery/images": {
      "post": {
        "operationId": "uploadGalleryImage",
        "tags": [
          "Projects"
        ],
        "summary": "Add a screenshot to the gallery",
        "description": "Everyone who can edit the project (owner, or owners and admins of its studio). PNG, JPEG or WebP up to 2 MB, at least 480×270 pixels, no animation. The server removes all metadata (EXIF including GPS position, XMP, IPTC, comments) and gives the file a random name. At most 20 images and videos per project. Set a caption with the change route.\n\nNot available with access tokens.\n\nRate limit: 30 uploads per hour per account (all images together).",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Added at the end of the gallery",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GalleryItem"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_image`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `gallery_full`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error codes: `unsupported_media_type`, `invalid_image`, `animated_image`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `image_too_small`, `invalid_aspect_ratio`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 uploads per hour per account (all images together). Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `storage_disabled`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 uploads per hour per account (all images together)",
        "requestBody": {
          "required": true,
          "description": "The image",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      }
    },
    "/v1/projects/{slug}/gallery/videos": {
      "post": {
        "operationId": "addGalleryVideo",
        "tags": [
          "Projects"
        ],
        "summary": "Add a YouTube video to the gallery",
        "description": "Everyone who can edit the project (owner, or owners and admins of its studio). Links like `https://www.youtube.com/watch?v=…`, `https://youtu.be/…` or Shorts. The portal loads the video from youtube-nocookie.com only after a click.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Added at the end of the gallery",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GalleryItem"
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `gallery_full`, `video_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `unsupported_video_url`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "YouTube link",
                    "maxLength": 300
                  },
                  "caption": {
                    "type": "string",
                    "maxLength": 140
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/projects/{slug}/gallery/{id}": {
      "patch": {
        "operationId": "updateGalleryItem",
        "tags": [
          "Projects"
        ],
        "summary": "Change caption or position",
        "description": "Everyone who can edit the project (owner, or owners and admins of its studio). `position` moves the item (0 = first).\n\nNot available with access tokens.\n\nRate limit: 120 edits per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gallery item ID (16 hex characters)",
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{16}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "The whole gallery in the new order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gallery": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GalleryItem"
                      }
                    }
                  },
                  "required": [
                    "gallery"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 edits per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "120 edits per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "caption": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "position": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteGalleryItem",
        "tags": [
          "Projects"
        ],
        "summary": "Remove an item from the gallery",
        "description": "Everyone who can edit the project (owner, or owners and admins of its studio). Images are deleted, unless a report about them is still open or moderation removed them (kept for an appeal).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gallery item ID (16 hex characters)",
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{16}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/status": {
      "get": {
        "operationId": "getStatus",
        "tags": [
          "Meta"
        ],
        "summary": "Platform status",
        "description": "What the status page in the portal shows: the state of the website, portal, web API, developer docs and MCP server, checked every minute from the GAMPEON server, with 90 days of daily history, incidents of the last 7 days and the community game servers that are online.\n\nThe checks run on the same server as the services. When that whole server is down, there is no data for that time, except for the web API, where missing minutes count as down. Answers are cached for 15 seconds.\n\nRate limit: 120 per minute per IP.",
        "security": [],
        "responses": {
          "200": {
            "description": "Current status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "operational",
                        "degraded",
                        "outage",
                        "unknown"
                      ],
                      "description": "Summary over all services"
                    },
                    "monitoring": {
                      "type": "boolean",
                      "description": "`false`: this API instance does not run checks (local development), `services` is empty"
                    },
                    "updatedAt": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "format": "int64",
                          "description": "Time of the latest check"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "intervalMs": {
                      "type": "integer",
                      "description": "How often the services are checked"
                    },
                    "services": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "website",
                              "portal",
                              "api",
                              "docs",
                              "mcp"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "link": {
                            "type": "string",
                            "description": "Page for people",
                            "format": "uri"
                          },
                          "state": {
                            "type": "string",
                            "enum": [
                              "up",
                              "degraded",
                              "down",
                              "unknown"
                            ],
                            "description": "`unknown`: no check in the last 3 minutes. A single failed check shows as `degraded`, two in a row as `down`."
                          },
                          "note": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Short reason when not up, e.g. `Timed out`, `HTTP 502`, `Docs not loaded`"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "ms": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "description": "Response time of the latest check in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "checkedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Time of the latest check"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "uptime": {
                            "type": "object",
                            "properties": {
                              "day": {
                                "oneOf": [
                                  {
                                    "type": "number",
                                    "description": "Availability in percent (degraded counts as available), `null` without data"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "week": {
                                "oneOf": [
                                  {
                                    "type": "number",
                                    "description": "Availability in percent (degraded counts as available), `null` without data"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "days90": {
                                "oneOf": [
                                  {
                                    "type": "number",
                                    "description": "Availability in percent (degraded counts as available), `null` without data"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              }
                            },
                            "required": [
                              "day",
                              "week",
                              "days90"
                            ]
                          },
                          "days": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "day": {
                                  "type": "string",
                                  "description": "UTC day `YYYY-MM-DD`"
                                },
                                "up": {
                                  "type": "integer",
                                  "description": "Minutes up"
                                },
                                "degraded": {
                                  "type": "integer",
                                  "description": "Minutes degraded"
                                },
                                "down": {
                                  "type": "integer",
                                  "description": "Minutes down"
                                }
                              },
                              "required": [
                                "day",
                                "up",
                                "degraded",
                                "down"
                              ]
                            },
                            "description": "90 days, oldest first. All zero: no data that day."
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "description",
                          "link",
                          "state",
                          "note",
                          "ms",
                          "checkedAt",
                          "uptime",
                          "days"
                        ]
                      }
                    },
                    "games": {
                      "type": "object",
                      "properties": {
                        "servers": {
                          "type": "integer",
                          "description": "Community game servers online (heartbeat in the last 90 seconds)"
                        },
                        "players": {
                          "type": "integer",
                          "description": "Players on those servers"
                        },
                        "top": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "players": {
                                "type": "integer"
                              },
                              "maxPlayers": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "id",
                              "name",
                              "players",
                              "maxPlayers"
                            ]
                          },
                          "description": "Up to 5 servers with the most players"
                        },
                        "history": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "at": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Start of the 15-minute step"
                              },
                              "players": {
                                "type": "integer",
                                "description": "Most players in that step"
                              },
                              "servers": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "at",
                              "players",
                              "servers"
                            ]
                          },
                          "description": "Last 24 hours"
                        }
                      },
                      "required": [
                        "servers",
                        "players",
                        "top",
                        "history"
                      ]
                    },
                    "incidents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "service": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string",
                            "enum": [
                              "degraded",
                              "down"
                            ]
                          },
                          "from": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "to": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "`null`: still ongoing"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "minutes": {
                            "type": "integer"
                          },
                          "note": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "service",
                          "name",
                          "state",
                          "from",
                          "to",
                          "minutes",
                          "note"
                        ]
                      },
                      "description": "Disruptions of 2 minutes or longer in the last 7 days, newest first (up to 20)"
                    }
                  },
                  "required": [
                    "status",
                    "monitoring",
                    "updatedAt",
                    "intervalMs",
                    "services",
                    "games",
                    "incidents"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Limit: 120 per minute per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "120 per minute per IP"
      }
    },
    "/v1/communities": {
      "get": {
        "operationId": "listCommunities",
        "tags": [
          "Communities"
        ],
        "summary": "Discover communities",
        "description": "Open communities of published studios and projects, most members first (up to 50). Accounts under 16 do not see communities for 16+. With login, `joined` says whether you are a member.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in name and description",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Communities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "communities": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "name": {
                                "type": "string"
                              },
                              "description": {
                                "type": "string"
                              },
                              "icon": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "description": "Image path under `/v1/media/` (studio logo or project key art)"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "cover": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "description": "Banner image under `/v1/media/`: the cover of the studio or the project. `null` if there is none"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "owner": {
                                "type": "object",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "studio",
                                      "project"
                                    ]
                                  },
                                  "slug": {
                                    "type": "string",
                                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                                  },
                                  "name": {
                                    "type": "string"
                                  }
                                },
                                "required": [
                                  "type",
                                  "slug",
                                  "name"
                                ]
                              },
                              "joinMode": {
                                "type": "string",
                                "enum": [
                                  "open",
                                  "invite"
                                ],
                                "description": "`open`: anyone who meets the minimum age can join. `invite`: only with an invite link"
                              },
                              "minAge": {
                                "type": "integer",
                                "description": "13 or 16. Communities for 18+ come only with real age verification"
                              },
                              "members": {
                                "type": "integer"
                              },
                              "players": {
                                "type": "integer",
                                "description": "Players online right now on servers of the project (or of all projects of the studio)"
                              },
                              "createdAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              }
                            },
                            "required": [
                              "id",
                              "name",
                              "description",
                              "icon",
                              "cover",
                              "owner",
                              "joinMode",
                              "minAge",
                              "members",
                              "players",
                              "createdAt"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "joined": {
                                "type": "boolean"
                              }
                            },
                            "required": [
                              "joined"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "communities"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      },
      "post": {
        "operationId": "createCommunity",
        "tags": [
          "Communities"
        ],
        "summary": "Turn on the community of a studio or project",
        "description": "Optional for every studio and project, at most one each. Studio owners and admins, or people who can edit the project. Starts with the roles `everyone` and `Moderator`, the categories Information (#welcome, #announcements, read-only) and Chat (#general, #help). Managers of the studio or project always have every permission and are members automatically.\n\nNot available with access tokens.\n\nRate limit: 5 per hour per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `studio_hidden`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `studio_not_found`, `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `community_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 5 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "5 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "studio": {
                    "type": "string",
                    "description": "Studio slug",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                  },
                  "project": {
                    "type": "string",
                    "description": "Project slug",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                  },
                  "joinMode": {
                    "type": "string",
                    "enum": [
                      "open",
                      "invite"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/me/communities": {
      "get": {
        "operationId": "listMyCommunities",
        "tags": [
          "Communities"
        ],
        "summary": "Your communities",
        "description": "Communities you joined plus those of studios and projects you manage, oldest first, with `unread` and `mentions` over the channels you can see. `manageable` lists studios and projects you manage and their community ID (or `null`), for a create button. Poll every 30 seconds at most until real-time updates arrive.\n\nNot available with access tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Communities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "communities": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "name": {
                                "type": "string"
                              },
                              "description": {
                                "type": "string"
                              },
                              "icon": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "description": "Image path under `/v1/media/` (studio logo or project key art)"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "cover": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "description": "Banner image under `/v1/media/`: the cover of the studio or the project. `null` if there is none"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "owner": {
                                "type": "object",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "studio",
                                      "project"
                                    ]
                                  },
                                  "slug": {
                                    "type": "string",
                                    "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                                  },
                                  "name": {
                                    "type": "string"
                                  }
                                },
                                "required": [
                                  "type",
                                  "slug",
                                  "name"
                                ]
                              },
                              "joinMode": {
                                "type": "string",
                                "enum": [
                                  "open",
                                  "invite"
                                ],
                                "description": "`open`: anyone who meets the minimum age can join. `invite`: only with an invite link"
                              },
                              "minAge": {
                                "type": "integer",
                                "description": "13 or 16. Communities for 18+ come only with real age verification"
                              },
                              "members": {
                                "type": "integer"
                              },
                              "players": {
                                "type": "integer",
                                "description": "Players online right now on servers of the project (or of all projects of the studio)"
                              },
                              "createdAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              }
                            },
                            "required": [
                              "id",
                              "name",
                              "description",
                              "icon",
                              "cover",
                              "owner",
                              "joinMode",
                              "minAge",
                              "members",
                              "players",
                              "createdAt"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "admin": {
                                "type": "boolean"
                              },
                              "hidden": {
                                "type": "boolean",
                                "description": "Hidden by GAMPEON or the studio or project is hidden"
                              },
                              "unread": {
                                "type": "boolean"
                              },
                              "mentions": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "admin",
                              "hidden",
                              "unread",
                              "mentions"
                            ]
                          }
                        ]
                      }
                    },
                    "manageable": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "studio",
                              "project"
                            ]
                          },
                          "slug": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "icon": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Image path under `/v1/media/` (studio logo or project key art)"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "community": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "type",
                          "slug",
                          "name",
                          "icon",
                          "community"
                        ]
                      }
                    }
                  },
                  "required": [
                    "communities",
                    "manageable"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}": {
      "get": {
        "operationId": "getCommunity",
        "tags": [
          "Communities"
        ],
        "summary": "A community",
        "description": "Without membership only a preview of open communities. Members also get `me` (their permissions in the community, roles, timeout), the channels they can see with their permissions and unread state, and all roles. `permissions` are bitmasks, see `BITMASK` in the role schema.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {},
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Community",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "icon": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Image path under `/v1/media/` (studio logo or project key art)"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "cover": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Banner image under `/v1/media/`: the cover of the studio or the project. `null` if there is none"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "owner": {
                          "type": "object",
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "studio",
                                "project"
                              ]
                            },
                            "slug": {
                              "type": "string",
                              "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                            },
                            "name": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "type",
                            "slug",
                            "name"
                          ]
                        },
                        "joinMode": {
                          "type": "string",
                          "enum": [
                            "open",
                            "invite"
                          ],
                          "description": "`open`: anyone who meets the minimum age can join. `invite`: only with an invite link"
                        },
                        "minAge": {
                          "type": "integer",
                          "description": "13 or 16. Communities for 18+ come only with real age verification"
                        },
                        "members": {
                          "type": "integer"
                        },
                        "players": {
                          "type": "integer",
                          "description": "Players online right now on servers of the project (or of all projects of the studio)"
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        }
                      },
                      "required": [
                        "id",
                        "name",
                        "description",
                        "icon",
                        "cover",
                        "owner",
                        "joinMode",
                        "minAge",
                        "members",
                        "players",
                        "createdAt"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "rules": {
                          "type": "string"
                        },
                        "member": {
                          "type": "boolean"
                        },
                        "banned": {
                          "type": "boolean"
                        },
                        "hidden": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "at": {
                                  "type": "integer",
                                  "format": "int64",
                                  "description": "Unix time in milliseconds"
                                },
                                "reason": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "at",
                                "reason"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "servers": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "players": {
                                "type": "integer"
                              },
                              "maxPlayers": {
                                "type": "integer"
                              },
                              "project": {
                                "type": "object",
                                "properties": {
                                  "slug": {
                                    "type": "string"
                                  },
                                  "title": {
                                    "type": "string"
                                  }
                                },
                                "required": [
                                  "slug",
                                  "title"
                                ]
                              }
                            },
                            "required": [
                              "id",
                              "name",
                              "players",
                              "maxPlayers",
                              "project"
                            ]
                          },
                          "description": "Online servers, most players first (up to 10)"
                        },
                        "me": {
                          "type": "object",
                          "properties": {
                            "permissions": {
                              "type": "integer",
                              "description": "Bitmask of permissions, see the table in the Communities section",
                              "minimum": 0
                            },
                            "admin": {
                              "type": "boolean"
                            },
                            "owner": {
                              "type": "boolean"
                            },
                            "team": {
                              "type": "boolean"
                            },
                            "roles": {
                              "type": "array",
                              "items": {
                                "type": "integer"
                              }
                            },
                            "timeoutUntil": {
                              "oneOf": [
                                {
                                  "type": "integer",
                                  "format": "int64",
                                  "description": "Unix time in milliseconds"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "top": {
                              "oneOf": [
                                {
                                  "type": "integer",
                                  "description": "Your highest role position, `null` for managers"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          },
                          "required": [
                            "permissions",
                            "admin",
                            "owner",
                            "team",
                            "roles",
                            "timeoutUntil",
                            "top"
                          ]
                        },
                        "channels": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "category",
                                  "text",
                                  "announcement",
                                  "voice"
                                ]
                              },
                              "name": {
                                "type": "string"
                              },
                              "topic": {
                                "type": "string"
                              },
                              "parentId": {
                                "oneOf": [
                                  {
                                    "type": "integer",
                                    "description": "Category"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "position": {
                                "type": "integer"
                              },
                              "slowmode": {
                                "type": "integer",
                                "description": "Seconds between two messages of the same person. 0 is off"
                              },
                              "permissions": {
                                "type": "integer",
                                "description": "Your effective permissions in this channel (see `permissions` on the community)"
                              },
                              "lastMessageId": {
                                "type": "integer"
                              },
                              "unread": {
                                "type": "boolean"
                              },
                              "mentions": {
                                "type": "integer",
                                "description": "Unread messages that mention you"
                              },
                              "userLimit": {
                                "type": "integer",
                                "description": "Only voice channels: most people at once, 0 is no limit of the community"
                              },
                              "overrides": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "roleId": {
                                      "type": "integer"
                                    },
                                    "allow": {
                                      "type": "integer",
                                      "description": "Bits allowed in this channel"
                                    },
                                    "deny": {
                                      "type": "integer",
                                      "description": "Bits denied in this channel"
                                    }
                                  },
                                  "required": [
                                    "roleId",
                                    "allow",
                                    "deny"
                                  ]
                                },
                                "description": "Only with `manageChannels`"
                              }
                            },
                            "required": [
                              "id",
                              "kind",
                              "name",
                              "topic",
                              "parentId",
                              "position",
                              "slowmode",
                              "permissions",
                              "lastMessageId",
                              "unread",
                              "mentions"
                            ]
                          }
                        },
                        "roles": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "name": {
                                "type": "string"
                              },
                              "color": {
                                "oneOf": [
                                  {
                                    "type": "string",
                                    "pattern": "^#[0-9a-fA-F]{6}$"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "position": {
                                "type": "integer",
                                "description": "Higher is more powerful. `everyone` is always 0"
                              },
                              "permissions": {
                                "type": "integer",
                                "description": "Bitmask of permissions, see the table in the Communities section",
                                "minimum": 0
                              },
                              "hoist": {
                                "type": "boolean",
                                "description": "Shown as its own group in the member list"
                              },
                              "everyone": {
                                "type": "boolean",
                                "description": "The default role every member has"
                              },
                              "members": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "id",
                              "name",
                              "color",
                              "position",
                              "permissions",
                              "hoist",
                              "everyone",
                              "members"
                            ]
                          }
                        }
                      },
                      "required": [
                        "rules",
                        "member",
                        "banned",
                        "hidden",
                        "servers"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "optional"
      },
      "patch": {
        "operationId": "updateCommunity",
        "tags": [
          "Communities"
        ],
        "summary": "Change a community",
        "description": "Needs `manageCommunity`.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Changed fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "updated"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `community_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 48
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "rules": {
                    "type": "string",
                    "description": "Shown before joining. People must accept them to join",
                    "maxLength": 2000
                  },
                  "joinMode": {
                    "type": "string",
                    "enum": [
                      "open",
                      "invite"
                    ]
                  },
                  "minAge": {
                    "type": "integer",
                    "enum": [
                      13,
                      16
                    ],
                    "description": "Accounts under 16 cannot join or see communities for 16+"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteCommunity",
        "tags": [
          "Communities"
        ],
        "summary": "Delete a community",
        "description": "Only the owner of the studio or project. Deletes all channels, roles and messages. Copies in reports to GAMPEON stay.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/members": {
      "post": {
        "operationId": "joinCommunity",
        "tags": [
          "Communities"
        ],
        "summary": "Join an open community",
        "description": "For `invite` communities use `POST /v1/community-invites/:code`. Messages from before joining count as read.\n\nNot available with access tokens.\n\nRate limit: 20 joins per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Joined (200 if you already were a member)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "joined": {
                      "const": true
                    }
                  },
                  "required": [
                    "id",
                    "joined"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `banned`, `age_restricted`, `invite_required`, `community_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `rules_not_accepted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 joins per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 joins per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "acceptRules": {
                    "const": true,
                    "description": "Required when the community has rules"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listCommunityMembers",
        "tags": [
          "Communities"
        ],
        "summary": "Members",
        "description": "Members only. Up to 1000, by display name. Online status comes with real-time updates.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Members",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string",
                            "pattern": "^[a-z0-9_]{3,24}$"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "avatar": {
                            "oneOf": [
                              {
                                "type": "string",
                                "description": "Image path under `/v1/media/` (studio logo or project key art)"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "joinedAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "roles": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            }
                          },
                          "admin": {
                            "type": "boolean",
                            "description": "Manages the studio or project: has every permission, cannot be kicked, banned or timed out"
                          },
                          "team": {
                            "type": "boolean",
                            "description": "Member of the studio or project team. Shown as a Team badge so nobody can pretend to be the team"
                          },
                          "owner": {
                            "type": "boolean"
                          },
                          "timeoutUntil": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "avatar",
                          "joinedAt",
                          "roles",
                          "admin",
                          "team",
                          "owner",
                          "timeoutUntil"
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "members",
                    "total"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/members/{handle}": {
      "patch": {
        "operationId": "updateCommunityMember",
        "tags": [
          "Communities"
        ],
        "summary": "Change roles or time out a member",
        "description": "`roles` (all role IDs the member should have, without `everyone`) needs `manageRoles`; you can only add or remove roles below your highest role. `timeoutMinutes` needs `timeoutMembers` and a highest role above theirs; 0 ends the timeout. Timed out members can only read. The person gets a notification with the reason.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the member",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "What changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "roles": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    },
                    "timeoutUntil": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": []
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "roles": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  },
                  "timeoutMinutes": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10080
                  },
                  "reason": {
                    "type": "string",
                    "description": "Optional reason, shown to the person affected",
                    "maxLength": 300
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "removeCommunityMember",
        "tags": [
          "Communities"
        ],
        "summary": "Leave, or kick a member",
        "description": "Your own handle: leave. Another handle: kick (needs `kickMembers` and a highest role above theirs). Kicked people can join again. Optional JSON body `{reason}` for the notification.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the member",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/bans": {
      "get": {
        "operationId": "listCommunityBans",
        "tags": [
          "Communities"
        ],
        "summary": "Bans",
        "description": "Needs `banMembers`.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 500, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bans": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "handle": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "bannedBy": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "handle",
                          "displayName",
                          "reason",
                          "createdAt",
                          "bannedBy"
                        ]
                      }
                    }
                  },
                  "required": [
                    "bans"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "post": {
        "operationId": "banCommunityMember",
        "tags": [
          "Communities"
        ],
        "summary": "Ban someone",
        "description": "Needs `banMembers` and a highest role above theirs. Removes them and blocks joining again, also with invites. `deleteMessagesHours` removes their messages of the last 1, 24 or 168 hours.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Banned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "banned": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "banned"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string",
                    "pattern": "^[a-z0-9_]{3,24}$"
                  },
                  "reason": {
                    "type": "string",
                    "description": "Optional reason, shown to the person affected",
                    "maxLength": 300
                  },
                  "deleteMessagesHours": {
                    "type": "integer",
                    "description": "0, 1, 24 or 168"
                  }
                },
                "required": [
                  "handle"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/bans/{handle}": {
      "delete": {
        "operationId": "unbanCommunityMember",
        "tags": [
          "Communities"
        ],
        "summary": "Lift a ban",
        "description": "Needs `banMembers`.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the member",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Lifted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/roles": {
      "post": {
        "operationId": "createCommunityRole",
        "tags": [
          "Communities"
        ],
        "summary": "Create a role",
        "description": "Needs `manageRoles`. New roles start at the bottom (position 1) with the permissions of `everyone`. You can only grant permissions you have. Up to 50 roles.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "color": {
                      "oneOf": [
                        {
                          "type": "string",
                          "pattern": "^#[0-9a-fA-F]{6}$"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "position": {
                      "type": "integer",
                      "description": "Higher is more powerful. `everyone` is always 0"
                    },
                    "permissions": {
                      "type": "integer",
                      "description": "Bitmask of permissions, see the table in the Communities section",
                      "minimum": 0
                    },
                    "hoist": {
                      "type": "boolean",
                      "description": "Shown as its own group in the member list"
                    },
                    "everyone": {
                      "type": "boolean",
                      "description": "The default role every member has"
                    },
                    "members": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "color",
                    "position",
                    "permissions",
                    "hoist",
                    "everyone",
                    "members"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `role_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 32
                  },
                  "color": {
                    "oneOf": [
                      {
                        "type": "string",
                        "pattern": "^#[0-9a-fA-F]{6}$"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "permissions": {
                    "type": "integer",
                    "description": "Bitmask of permissions, see the table in the Communities section",
                    "minimum": 0
                  },
                  "hoist": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/roles/{roleId}": {
      "patch": {
        "operationId": "updateCommunityRole",
        "tags": [
          "Communities"
        ],
        "summary": "Change a role",
        "description": "Needs `manageRoles`, and the role must be below your highest role (except `everyone`). `position` moves it (1 is just above `everyone`); only below your highest role.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "roleId",
            "in": "path",
            "required": true,
            "description": "Role ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Role",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "color": {
                      "oneOf": [
                        {
                          "type": "string",
                          "pattern": "^#[0-9a-fA-F]{6}$"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "position": {
                      "type": "integer",
                      "description": "Higher is more powerful. `everyone` is always 0"
                    },
                    "permissions": {
                      "type": "integer",
                      "description": "Bitmask of permissions, see the table in the Communities section",
                      "minimum": 0
                    },
                    "hoist": {
                      "type": "boolean",
                      "description": "Shown as its own group in the member list"
                    },
                    "everyone": {
                      "type": "boolean",
                      "description": "The default role every member has"
                    },
                    "members": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "color",
                    "position",
                    "permissions",
                    "hoist",
                    "everyone",
                    "members"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 32
                  },
                  "color": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "permissions": {
                    "type": "integer",
                    "description": "Bitmask of permissions, see the table in the Communities section",
                    "minimum": 0
                  },
                  "hoist": {
                    "type": "boolean"
                  },
                  "position": {
                    "type": "integer",
                    "minimum": 1
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteCommunityRole",
        "tags": [
          "Communities"
        ],
        "summary": "Delete a role",
        "description": "Needs `manageRoles`, only roles below your highest role. `everyone` cannot be deleted.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "roleId",
            "in": "path",
            "required": true,
            "description": "Role ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/channels": {
      "post": {
        "operationId": "createCommunityChannel",
        "tags": [
          "Communities"
        ],
        "summary": "Create a channel or category",
        "description": "Needs `manageChannels`. Text channel names are lowercase (spaces become `-`). Channels inherit the overrides of their category, then apply their own. Announcement channels start read-only for `everyone`. Voice channels have no messages; `userLimit` caps how many people can be in one at once. Only `viewChannels`, `sendMessages`, `manageMessages`, `connect`, `speak`, `stream` can be set per channel. Up to 100 channels.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `channel_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "category",
                      "text",
                      "announcement",
                      "voice"
                    ]
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 32
                  },
                  "topic": {
                    "type": "string",
                    "maxLength": 300
                  },
                  "parentId": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "description": "Category ID"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "overrides": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "roleId": {
                          "type": "integer"
                        },
                        "allow": {
                          "type": "integer",
                          "description": "Bits allowed in this channel"
                        },
                        "deny": {
                          "type": "integer",
                          "description": "Bits denied in this channel"
                        }
                      },
                      "required": [
                        "roleId",
                        "allow",
                        "deny"
                      ]
                    }
                  },
                  "userLimit": {
                    "type": "integer",
                    "description": "Only voice channels. 0 is no limit",
                    "minimum": 0,
                    "maximum": 99
                  }
                },
                "required": [
                  "kind",
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/channels/{channelId}": {
      "patch": {
        "operationId": "updateCommunityChannel",
        "tags": [
          "Communities"
        ],
        "summary": "Change a channel",
        "description": "Needs `manageChannels`. `overrides` replaces all overrides of the channel. `position` is the index in the full channel list (categories included).\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Changed fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "updated"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "topic": {
                    "type": "string",
                    "maxLength": 300
                  },
                  "slowmode": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 21600
                  },
                  "parentId": {
                    "oneOf": [
                      {
                        "type": "integer"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "overrides": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "roleId": {
                          "type": "integer"
                        },
                        "allow": {
                          "type": "integer",
                          "description": "Bits allowed in this channel"
                        },
                        "deny": {
                          "type": "integer",
                          "description": "Bits denied in this channel"
                        }
                      },
                      "required": [
                        "roleId",
                        "allow",
                        "deny"
                      ]
                    }
                  },
                  "position": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "userLimit": {
                    "type": "integer",
                    "description": "Only voice channels. 0 is no limit",
                    "minimum": 0,
                    "maximum": 99
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteCommunityChannel",
        "tags": [
          "Communities"
        ],
        "summary": "Delete a channel",
        "description": "Needs `manageChannels`. Deletes its messages. Channels in a deleted category move out of it.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/channels/{channelId}/messages": {
      "get": {
        "operationId": "listChannelMessages",
        "tags": [
          "Communities"
        ],
        "summary": "Messages of a channel",
        "description": "Without parameters the newest page (oldest first inside the page); `before` loads older pages. For updates, poll every 2 to 5 seconds with `updatedSince` set to `now` from the previous answer: you get new, edited and removed messages since then (removed ones as placeholders). Slow down or stop polling while the window is hidden.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Message ID: only older messages",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "required": false,
            "description": "Time from `now` of the last answer",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 to 100 (default 50)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "oneOf": [
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "channelId": {
                                "type": "integer"
                              },
                              "author": {
                                "oneOf": [
                                  {
                                    "type": "object",
                                    "properties": {
                                      "handle": {
                                        "type": "string",
                                        "pattern": "^[a-z0-9_]{3,24}$"
                                      },
                                      "displayName": {
                                        "type": "string"
                                      },
                                      "avatar": {
                                        "oneOf": [
                                          {
                                            "type": "string",
                                            "description": "Image path under `/v1/media/` (studio logo or project key art)"
                                          },
                                          {
                                            "type": "null"
                                          }
                                        ]
                                      }
                                    },
                                    "required": [
                                      "handle",
                                      "displayName",
                                      "avatar"
                                    ]
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "body": {
                                "type": "string"
                              },
                              "replyTo": {
                                "oneOf": [
                                  {
                                    "oneOf": [
                                      {
                                        "type": "object",
                                        "properties": {
                                          "id": {
                                            "type": "integer"
                                          },
                                          "author": {
                                            "oneOf": [
                                              {
                                                "type": "object",
                                                "properties": {
                                                  "handle": {
                                                    "type": "string"
                                                  },
                                                  "displayName": {
                                                    "type": "string"
                                                  }
                                                },
                                                "required": [
                                                  "handle",
                                                  "displayName"
                                                ]
                                              },
                                              {
                                                "type": "null"
                                              }
                                            ]
                                          },
                                          "excerpt": {
                                            "type": "string"
                                          }
                                        },
                                        "required": [
                                          "id",
                                          "author",
                                          "excerpt"
                                        ]
                                      },
                                      {
                                        "type": "object",
                                        "properties": {
                                          "id": {
                                            "type": "integer"
                                          },
                                          "removed": {
                                            "const": true
                                          }
                                        },
                                        "required": [
                                          "id",
                                          "removed"
                                        ]
                                      }
                                    ]
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "mentions": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "description": "Handles mentioned with @"
                              },
                              "createdAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "updatedAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "editedAt": {
                                "oneOf": [
                                  {
                                    "type": "integer",
                                    "format": "int64",
                                    "description": "Unix time in milliseconds"
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              }
                            },
                            "required": [
                              "id",
                              "channelId",
                              "author",
                              "body",
                              "replyTo",
                              "mentions",
                              "createdAt",
                              "updatedAt",
                              "editedAt"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "channelId": {
                                "type": "integer"
                              },
                              "removed": {
                                "const": true
                              },
                              "removedBy": {
                                "type": "string",
                                "enum": [
                                  "author",
                                  "community",
                                  "platform"
                                ],
                                "description": "Deleted by the author, removed by community moderators or by GAMPEON"
                              },
                              "createdAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              "updatedAt": {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              }
                            },
                            "required": [
                              "id",
                              "channelId",
                              "removed",
                              "removedBy",
                              "createdAt",
                              "updatedAt"
                            ]
                          }
                        ]
                      }
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Server time, use it for the next `updatedSince`"
                    }
                  },
                  "required": [
                    "messages",
                    "hasMore",
                    "now"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "post": {
        "operationId": "sendChannelMessage",
        "tags": [
          "Communities"
        ],
        "summary": "Send a message",
        "description": "Needs `sendMessages` in the channel and no timeout. Up to 2000 characters and 5 links. `@handle` mentions members who can see the channel. Slow mode applies unless you have `manageMessages`.\n\nNot available with access tokens.\n\nRate limit: 10 per 10 seconds and 600 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "channelId": {
                          "type": "integer"
                        },
                        "author": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "handle": {
                                  "type": "string",
                                  "pattern": "^[a-z0-9_]{3,24}$"
                                },
                                "displayName": {
                                  "type": "string"
                                },
                                "avatar": {
                                  "oneOf": [
                                    {
                                      "type": "string",
                                      "description": "Image path under `/v1/media/` (studio logo or project key art)"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "handle",
                                "displayName",
                                "avatar"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "body": {
                          "type": "string"
                        },
                        "replyTo": {
                          "oneOf": [
                            {
                              "oneOf": [
                                {
                                  "type": "object",
                                  "properties": {
                                    "id": {
                                      "type": "integer"
                                    },
                                    "author": {
                                      "oneOf": [
                                        {
                                          "type": "object",
                                          "properties": {
                                            "handle": {
                                              "type": "string"
                                            },
                                            "displayName": {
                                              "type": "string"
                                            }
                                          },
                                          "required": [
                                            "handle",
                                            "displayName"
                                          ]
                                        },
                                        {
                                          "type": "null"
                                        }
                                      ]
                                    },
                                    "excerpt": {
                                      "type": "string"
                                    }
                                  },
                                  "required": [
                                    "id",
                                    "author",
                                    "excerpt"
                                  ]
                                },
                                {
                                  "type": "object",
                                  "properties": {
                                    "id": {
                                      "type": "integer"
                                    },
                                    "removed": {
                                      "const": true
                                    }
                                  },
                                  "required": [
                                    "id",
                                    "removed"
                                  ]
                                }
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "mentions": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Handles mentioned with @"
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "editedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "channelId",
                        "author",
                        "body",
                        "replyTo",
                        "mentions",
                        "createdAt",
                        "updatedAt",
                        "editedAt"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "channelId": {
                          "type": "integer"
                        },
                        "removed": {
                          "const": true
                        },
                        "removedBy": {
                          "type": "string",
                          "enum": [
                            "author",
                            "community",
                            "platform"
                          ],
                          "description": "Deleted by the author, removed by community moderators or by GAMPEON"
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        }
                      },
                      "required": [
                        "id",
                        "channelId",
                        "removed",
                        "removedBy",
                        "createdAt",
                        "updatedAt"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `too_many_links`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `timed_out`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per 10 seconds and 600 per hour per account. Error codes: `rate_limited`, `slowmode`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per 10 seconds and 600 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "replyTo": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "description": "Message ID in the same channel"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/channels/{channelId}/read": {
      "post": {
        "operationId": "markChannelRead",
        "tags": [
          "Communities"
        ],
        "summary": "Mark a channel as read",
        "description": "Up to `messageId` (default: the newest message). Never moves backwards.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Read state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lastReadId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "lastReadId"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "messageId": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/messages/{messageId}": {
      "patch": {
        "operationId": "editChannelMessage",
        "tags": [
          "Communities"
        ],
        "summary": "Edit your message",
        "description": "Not available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "Message ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Message",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "channelId": {
                          "type": "integer"
                        },
                        "author": {
                          "oneOf": [
                            {
                              "type": "object",
                              "properties": {
                                "handle": {
                                  "type": "string",
                                  "pattern": "^[a-z0-9_]{3,24}$"
                                },
                                "displayName": {
                                  "type": "string"
                                },
                                "avatar": {
                                  "oneOf": [
                                    {
                                      "type": "string",
                                      "description": "Image path under `/v1/media/` (studio logo or project key art)"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                }
                              },
                              "required": [
                                "handle",
                                "displayName",
                                "avatar"
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "body": {
                          "type": "string"
                        },
                        "replyTo": {
                          "oneOf": [
                            {
                              "oneOf": [
                                {
                                  "type": "object",
                                  "properties": {
                                    "id": {
                                      "type": "integer"
                                    },
                                    "author": {
                                      "oneOf": [
                                        {
                                          "type": "object",
                                          "properties": {
                                            "handle": {
                                              "type": "string"
                                            },
                                            "displayName": {
                                              "type": "string"
                                            }
                                          },
                                          "required": [
                                            "handle",
                                            "displayName"
                                          ]
                                        },
                                        {
                                          "type": "null"
                                        }
                                      ]
                                    },
                                    "excerpt": {
                                      "type": "string"
                                    }
                                  },
                                  "required": [
                                    "id",
                                    "author",
                                    "excerpt"
                                  ]
                                },
                                {
                                  "type": "object",
                                  "properties": {
                                    "id": {
                                      "type": "integer"
                                    },
                                    "removed": {
                                      "const": true
                                    }
                                  },
                                  "required": [
                                    "id",
                                    "removed"
                                  ]
                                }
                              ]
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "mentions": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Handles mentioned with @"
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "editedAt": {
                          "oneOf": [
                            {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "channelId",
                        "author",
                        "body",
                        "replyTo",
                        "mentions",
                        "createdAt",
                        "updatedAt",
                        "editedAt"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "channelId": {
                          "type": "integer"
                        },
                        "removed": {
                          "const": true
                        },
                        "removedBy": {
                          "type": "string",
                          "enum": [
                            "author",
                            "community",
                            "platform"
                          ],
                          "description": "Deleted by the author, removed by community moderators or by GAMPEON"
                        },
                        "createdAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        "updatedAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        }
                      },
                      "required": [
                        "id",
                        "channelId",
                        "removed",
                        "removedBy",
                        "createdAt",
                        "updatedAt"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `forbidden`, `timed_out`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteChannelMessage",
        "tags": [
          "Communities"
        ],
        "summary": "Delete or remove a message",
        "description": "Your own: deleted right away (a placeholder stays). Others: needs `manageMessages` in the channel; the author gets a notification with the optional reason (JSON body `{reason}`). Messages of studio or project managers can only be removed by other managers or GAMPEON. The text of removed messages is deleted after 30 days unless a report is open.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "Message ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/invites": {
      "post": {
        "operationId": "createCommunityInvite",
        "tags": [
          "Communities"
        ],
        "summary": "Create an invite link",
        "description": "Needs `createInvites`. Links last 1 to 30 days (default 7). `maxUses` 0 means no limit. Share `https://gampeon.com/invite/<code>` once the launcher handles invite links.\n\nNot available with access tokens.\n\nRate limit: 20 per hour per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Invite",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "maxUses": {
                      "type": "integer"
                    },
                    "uses": {
                      "type": "integer"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "code",
                    "maxUses",
                    "uses",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `invite_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "maxUses": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000
                  },
                  "expiresInDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listCommunityInvites",
        "tags": [
          "Communities"
        ],
        "summary": "Active invite links",
        "description": "All of them with `manageCommunity`, otherwise your own.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Invites",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invites": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "maxUses": {
                            "type": "integer"
                          },
                          "uses": {
                            "type": "integer"
                          },
                          "expiresAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "createdBy": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "code",
                          "maxUses",
                          "uses",
                          "expiresAt",
                          "createdAt",
                          "createdBy"
                        ]
                      }
                    }
                  },
                  "required": [
                    "invites"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/invites/{code}": {
      "delete": {
        "operationId": "deleteCommunityInvite",
        "tags": [
          "Communities"
        ],
        "summary": "Revoke an invite link",
        "description": "Your own, or any with `manageCommunity`.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Invite code",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/community-invites/{code}": {
      "get": {
        "operationId": "getCommunityInvite",
        "tags": [
          "Communities"
        ],
        "summary": "Preview an invite",
        "description": "Name, icon, members and rules of the community behind an invite link.\n\nRate limit: 60 per hour per IP.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Invite code",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8}$"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Invite",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "community": {
                      "allOf": [
                        {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "integer"
                            },
                            "name": {
                              "type": "string"
                            },
                            "description": {
                              "type": "string"
                            },
                            "icon": {
                              "oneOf": [
                                {
                                  "type": "string",
                                  "description": "Image path under `/v1/media/` (studio logo or project key art)"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "cover": {
                              "oneOf": [
                                {
                                  "type": "string",
                                  "description": "Banner image under `/v1/media/`: the cover of the studio or the project. `null` if there is none"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "owner": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "enum": [
                                    "studio",
                                    "project"
                                  ]
                                },
                                "slug": {
                                  "type": "string",
                                  "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
                                },
                                "name": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "type",
                                "slug",
                                "name"
                              ]
                            },
                            "joinMode": {
                              "type": "string",
                              "enum": [
                                "open",
                                "invite"
                              ],
                              "description": "`open`: anyone who meets the minimum age can join. `invite`: only with an invite link"
                            },
                            "minAge": {
                              "type": "integer",
                              "description": "13 or 16. Communities for 18+ come only with real age verification"
                            },
                            "members": {
                              "type": "integer"
                            },
                            "players": {
                              "type": "integer",
                              "description": "Players online right now on servers of the project (or of all projects of the studio)"
                            },
                            "createdAt": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Unix time in milliseconds"
                            }
                          },
                          "required": [
                            "id",
                            "name",
                            "description",
                            "icon",
                            "cover",
                            "owner",
                            "joinMode",
                            "minAge",
                            "members",
                            "players",
                            "createdAt"
                          ]
                        },
                        {
                          "type": "object",
                          "properties": {
                            "rules": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "rules"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "code",
                    "expiresAt",
                    "community"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `invite_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 60 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "60 per hour per IP"
      },
      "post": {
        "operationId": "acceptCommunityInvite",
        "tags": [
          "Communities"
        ],
        "summary": "Join with an invite",
        "description": "Same rules as joining an open community: not banned, minimum age, rules accepted.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Invite code",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Joined (200 if you already were a member)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "joined": {
                      "const": true
                    }
                  },
                  "required": [
                    "id",
                    "joined"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `banned`, `age_restricted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `invite_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `rules_not_accepted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "acceptRules": {
                    "const": true,
                    "description": "Required when the community has rules"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/audit-log": {
      "get": {
        "operationId": "getCommunityAuditLog",
        "tags": [
          "Communities"
        ],
        "summary": "Audit log",
        "description": "Needs `viewAuditLog`. The last 200 changes: settings, channels, roles, kicks, bans, timeouts, removed messages, invites.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Entries, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "action": {
                            "type": "string"
                          },
                          "target": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "object",
                            "additionalProperties": {}
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "actor": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "actorName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "action",
                          "target",
                          "detail",
                          "createdAt",
                          "actor",
                          "actorName"
                        ]
                      }
                    }
                  },
                  "required": [
                    "entries"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/messages/{messageId}/reports": {
      "post": {
        "operationId": "reportChannelMessage",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report a message to GAMPEON",
        "description": "For illegal content or breaks of the GAMPEON terms (Art. 16 DSA), separate from the moderators of the community. GAMPEON keeps a copy of the message and the three before it. You get a confirmation and the result.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "Message ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you already reported it)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per hour per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "illegal_content",
                      "harassment",
                      "hate_speech",
                      "violence_threat",
                      "child_safety",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "description": "Defaults to your display name",
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "description": "Defaults to your email address",
                    "format": "email",
                    "maxLength": 254
                  },
                  "anonymous": {
                    "type": "boolean",
                    "description": "Only for `child_safety`: report without name and email"
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/community-reports": {
      "get": {
        "operationId": "listCommunityReports",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Reports on community messages",
        "description": "Urgent categories (`child_safety`, `violence_threat`) first (Art. 18 DSA).\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default `open`",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "actioned",
                "rejected"
              ]
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 200",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "message",
                              "voice"
                            ],
                            "description": "`voice`: a person in a voice channel; `context` lists who was there"
                          },
                          "messageId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "communityId": {
                            "oneOf": [
                              {
                                "type": "integer"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "community": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "snapshot": {
                            "type": "string"
                          },
                          "context": {
                            "type": "string",
                            "description": "Community, channel and the three messages before"
                          },
                          "category": {
                            "type": "string"
                          },
                          "explanation": {
                            "type": "string"
                          },
                          "reporterName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "authorHandle": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "authorName": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "removedAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "deleted": {
                            "type": "boolean"
                          },
                          "urgent": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "kind",
                          "messageId",
                          "communityId",
                          "community",
                          "snapshot",
                          "context",
                          "category",
                          "explanation",
                          "reporterName",
                          "status",
                          "createdAt",
                          "authorHandle",
                          "authorName",
                          "removedAt",
                          "deleted",
                          "urgent"
                        ]
                      }
                    }
                  },
                  "required": [
                    "reports"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator"
      }
    },
    "/v1/moderation/community-reports/{id}/decision": {
      "post": {
        "operationId": "decideCommunityReport",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Decide a report on a message",
        "description": "`remove` hides the message for everyone and sends the statement of reasons to the author (Art. 17 DSA); all open reports on it close. For voice reports there is nothing to remove: `remove` marks the report as actioned. Reporters get the result.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Report ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "remove",
                      "reject"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the people affected (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/moderation/communities/{id}/decision": {
      "post": {
        "operationId": "decideCommunity",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Hide or restore a community",
        "description": "For communities that break the terms as a whole. Managers of the studio or project get the statement of reasons.\n\nRequires the role moderator or admin.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Decided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisionId": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "decisionId",
                    "action"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `forbidden`, `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "moderator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "hide",
                      "restore"
                    ]
                  },
                  "statementOfReasons": {
                    "type": "string",
                    "description": "Statement of reasons, shown to the people affected (Art. 17 DSA)",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "legalGround": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action",
                  "statementOfReasons"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/channels/{channelId}/voice": {
      "post": {
        "operationId": "joinVoiceChannel",
        "tags": [
          "Voice"
        ],
        "summary": "Join a voice channel",
        "description": "Needs `connect` in the channel, no timeout and an age of 16 or older. Returns the address of the voice server and a token for this channel only, valid for 10 minutes to connect (LiveKit keeps the connection alive after that). The token allows the microphone only with `speak` (and when not muted by moderators) and screen sharing with sound only with `stream`. You can be in one voice channel at a time: joining another one disconnects you from the first. Full channels (`userLimit`) can only be joined with `moveMembers`; at most 100 people per channel.\n\nNot available with access tokens.\n\nRate limit: 30 per 10 minutes per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "WebSocket address of the voice server"
                    },
                    "token": {
                      "type": "string",
                      "description": "Access token for the voice server"
                    },
                    "room": {
                      "type": "string"
                    },
                    "identity": {
                      "type": "string",
                      "description": "Your random ID on the voice server"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Connect before this"
                    },
                    "permissions": {
                      "type": "integer",
                      "description": "Your permissions in this channel"
                    },
                    "muted": {
                      "type": "boolean",
                      "description": "Muted by moderators of the community"
                    },
                    "canSpeak": {
                      "type": "boolean"
                    },
                    "canStream": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "url",
                    "token",
                    "room",
                    "identity",
                    "expiresAt",
                    "permissions",
                    "muted",
                    "canSpeak",
                    "canStream"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `not_a_voice_channel`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `age_restricted`, `timed_out`, `missing_permission`, `banned`, `community_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `channel_full`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per 10 minutes per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `voice_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per 10 minutes per account"
      }
    },
    "/v1/communities/{id}/voice": {
      "get": {
        "operationId": "getCommunityVoice",
        "tags": [
          "Voice"
        ],
        "summary": "Who is in which voice channel",
        "description": "All voice channels you can see, with the people in them. `allowed` is false for accounts under 16 (`reason: age_restricted`): they see voice channels but cannot join. `enabled` is false while the voice server is not set up. Poll every 10 to 15 seconds at most until real-time updates arrive.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Voice channels",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "allowed": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "age_restricted"
                      ]
                    },
                    "channels": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "userLimit": {
                            "type": "integer",
                            "description": "0 is no limit"
                          },
                          "participants": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "handle": {
                                  "type": "string",
                                  "pattern": "^[a-z0-9_]{3,24}$"
                                },
                                "displayName": {
                                  "type": "string"
                                },
                                "avatar": {
                                  "oneOf": [
                                    {
                                      "type": "string"
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                },
                                "joinedAt": {
                                  "type": "integer",
                                  "format": "int64",
                                  "description": "Unix time in milliseconds"
                                },
                                "streaming": {
                                  "type": "boolean",
                                  "description": "Sharing a screen right now"
                                },
                                "muted": {
                                  "type": "boolean",
                                  "description": "Muted by moderators"
                                },
                                "team": {
                                  "type": "boolean",
                                  "description": "Member of the studio or project team"
                                }
                              },
                              "required": [
                                "handle",
                                "displayName",
                                "avatar",
                                "joinedAt",
                                "streaming",
                                "muted",
                                "team"
                              ]
                            }
                          }
                        },
                        "required": [
                          "id",
                          "userLimit",
                          "participants"
                        ]
                      }
                    }
                  },
                  "required": [
                    "enabled",
                    "allowed",
                    "channels"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/hooks/livekit": {
      "post": {
        "operationId": "livekitWebhook",
        "tags": [
          "Voice"
        ],
        "summary": "Voice server webhook (called by LiveKit only)",
        "description": "Signed with `Authorization: <JWT>` from the API key of the voice server, with the SHA-256 of the raw body. Handles `participant_joined`, `participant_left`, `track_published`, `track_unpublished` (screen sharing) and `room_finished`. Unknown participants are removed. Only active when voice is set up.",
        "security": [],
        "responses": {
          "200": {
            "description": "Processed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "const": true
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `invalid_signature`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `body_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "description": "e.g. `participant_joined`"
                  }
                },
                "required": [
                  "event"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/communities/{id}/voice/members/{handle}": {
      "patch": {
        "operationId": "muteVoiceMember",
        "tags": [
          "Voice"
        ],
        "summary": "Mute or unmute a member in voice",
        "description": "Needs `muteMembers`, and their highest role must be below yours. Applies to all voice channels of the community until someone lifts it. Muting turns their microphone off right away; after unmuting they turn it on themselves. In the audit log.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the member",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Muted state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "muted": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "muted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "muted": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "muted"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "disconnectVoiceMember",
        "tags": [
          "Voice"
        ],
        "summary": "Disconnect a member from voice",
        "description": "Your own handle: leave voice. Someone else: needs `moveMembers`, and their highest role must be below yours. They can join again (time them out or kick them to stop that). In the audit log.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Handle of the member",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9_]{3,24}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Disconnected"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `missing_permission`, `role_hierarchy`, `forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`, `not_in_voice`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/communities/{id}/channels/{channelId}/voice/reports": {
      "post": {
        "operationId": "reportVoiceMember",
        "tags": [
          "Reports and moderation"
        ],
        "summary": "Report someone in a voice channel to GAMPEON",
        "description": "For illegal behaviour or breaks of the GAMPEON terms in voice (Art. 16 DSA), separate from the moderators of the community. There are no recordings, so GAMPEON adds who was in the channel around `at` (from two hours before to 15 minutes after). Urgent categories come first. You get a confirmation and the result.\n\nNot available with access tokens.\n\nRate limit: 10 per hour per IP (shared with message reports).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Community ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Voice channel ID",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Report received (200 if you reported them in the last hour already)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reportId": {
                      "type": "string"
                    },
                    "status": {
                      "const": "open"
                    }
                  },
                  "required": [
                    "reportId",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `not_a_voice_channel`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `good_faith_confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 10 per hour per IP (shared with message reports). Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "10 per hour per IP (shared with message reports)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string",
                    "description": "The person you report",
                    "pattern": "^[a-z0-9_]{3,24}$"
                  },
                  "at": {
                    "type": "integer",
                    "format": "int64",
                    "description": "When it happened (default: now), within the last 7 days"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "illegal_content",
                      "harassment",
                      "hate_speech",
                      "violence_threat",
                      "child_safety",
                      "spam",
                      "copyright",
                      "other"
                    ]
                  },
                  "explanation": {
                    "type": "string",
                    "description": "What happened",
                    "minLength": 20,
                    "maxLength": 5000
                  },
                  "reporterName": {
                    "type": "string",
                    "description": "Defaults to your display name",
                    "maxLength": 120
                  },
                  "reporterEmail": {
                    "type": "string",
                    "description": "Defaults to your email address",
                    "format": "email",
                    "maxLength": 254
                  },
                  "anonymous": {
                    "type": "boolean",
                    "description": "Only for `child_safety`: report without name and email"
                  },
                  "goodFaith": {
                    "const": true,
                    "description": "Confirms the report is made in good faith"
                  }
                },
                "required": [
                  "handle",
                  "category",
                  "explanation",
                  "goodFaith"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/auth/launcher/approve": {
      "post": {
        "operationId": "approveLauncherSignIn",
        "tags": [
          "Accounts"
        ],
        "summary": "Approve a sign-in for the launcher",
        "description": "Called by the portal page that the launcher opens in the system browser, after you confirm that you want to sign in to the launcher. Returns a one-time code that the portal hands to the launcher with the link `gampeon://auth?code=…&state=…`. The code is valid for 2 minutes and only together with the secret `verifier` behind `challenge`, which never leaves the launcher (PKCE, RFC 7636, method `S256`).\n\nNot available with access tokens.\n\nRate limit: 20 per 15 minutes per account.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "One-time code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "description": "One-time code, 43 characters"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "code",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 20 per 15 minutes per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "20 per 15 minutes per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "challenge": {
                    "type": "string",
                    "description": "`BASE64URL(SHA-256(verifier))` without padding, 43 characters",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  }
                },
                "required": [
                  "challenge"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/auth/launcher/token": {
      "post": {
        "operationId": "completeLauncherSignIn",
        "tags": [
          "Accounts"
        ],
        "summary": "Complete a sign-in in the launcher",
        "description": "Exchanges the one-time code from `gampeon://auth` and the `verifier` for a session of its own (30 days). The code can be used once; a wrong `verifier` uses it up as well.\n\nRate limit: 30 per 15 minutes per IP.",
        "security": [],
        "responses": {
          "201": {
            "description": "Session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Bearer token for the `Authorization` header"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "token",
                    "expiresAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_code`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per 15 minutes per IP. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "30 per 15 minutes per IP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "One-time code from the portal",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  },
                  "verifier": {
                    "type": "string",
                    "description": "Random secret of 43 to 128 characters `A-Z a-z 0-9 - . _ ~` that the launcher created for this sign-in",
                    "pattern": "^[A-Za-z0-9._~-]{43,128}$"
                  }
                },
                "required": [
                  "code",
                  "verifier"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/servers/{id}/join": {
      "post": {
        "operationId": "joinServer",
        "tags": [
          "Servers"
        ],
        "summary": "Get a join token for a server",
        "description": "The launcher calls this right before it starts the game client and passes the token to the client in the environment variable `GAMPEON_JOIN_TOKEN`. The client sends it to the game server when it connects, and the game server checks it with `POST /v1/servers/{id}/join/verify`. Valid for 10 minutes, once, and only for this server.\n\nIf the server runs game packages of its project, `packages` lists their files and `packageGrant` allows downloading them with `GET /v1/packages/files/{sha256}` for 60 minutes. The launcher downloads missing files, checks their SHA-256 and starts the client with them.\n\nNot available with access tokens.\n\nRate limit: 30 per 10 minutes per account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Server ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Join token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Join token, 43 characters"
                    },
                    "address": {
                      "type": "string",
                      "description": "Host name or IP of the server"
                    },
                    "port": {
                      "type": "integer"
                    },
                    "expiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "packages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "version": {
                            "type": "string"
                          },
                          "engine": {
                            "type": "string",
                            "description": "GAMPEON version the package needs"
                          },
                          "size": {
                            "type": "integer",
                            "description": "Bytes of all files"
                          },
                          "files": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "path": {
                                  "type": "string",
                                  "description": "Path inside the package, such as `GamCity.uplugin`, `Content/Paks/WindowsClient/GamCity….pak` or `Nui/icons/cash.png`"
                                },
                                "size": {
                                  "type": "integer",
                                  "description": "Bytes",
                                  "minimum": 1
                                },
                                "sha256": {
                                  "type": "string",
                                  "description": "SHA-256, 64 hex characters"
                                }
                              },
                              "required": [
                                "path",
                                "size",
                                "sha256"
                              ]
                            }
                          }
                        },
                        "required": [
                          "name",
                          "version",
                          "engine",
                          "size",
                          "files"
                        ]
                      },
                      "description": "Game packages the server needs (empty without packages)"
                    },
                    "packageGrant": {
                      "type": "string",
                      "description": "Download grant for `packages`, only when there are packages"
                    },
                    "packageGrantExpiresAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    }
                  },
                  "required": [
                    "token",
                    "address",
                    "port",
                    "expiresAt",
                    "packages"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `server_offline`, `server_full`, `package_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per 10 minutes per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per 10 minutes per account"
      }
    },
    "/v1/servers/{id}/join/verify": {
      "post": {
        "operationId": "verifyJoinToken",
        "tags": [
          "Servers"
        ],
        "summary": "Check a join token",
        "description": "Called by the game server when a player connects with a join token. Answers who the player is and uses the token up. Unknown, expired, used tokens and tokens for another server are refused alike.\n\nRate limit: 1200 per minute per server.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Server ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-server-key",
            "in": "header",
            "required": true,
            "description": "Key from registering the server",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "serverKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Player",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Account ID. Stable, use it to keep the player's progress."
                        },
                        "handle": {
                          "type": "string",
                          "description": "Handle, can change"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "avatar": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Path of the profile picture, relative to the API"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "handle",
                        "displayName",
                        "avatar"
                      ]
                    },
                    "issuedAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "When the launcher asked for the token"
                    }
                  },
                  "required": [
                    "account",
                    "issuedAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_join_token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `invalid_server_key`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 1200 per minute per server. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "serverKey",
        "x-gampeon-rate-limit": "1200 per minute per server",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Join token from the client",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/projects/{slug}/packages": {
      "get": {
        "operationId": "listProjectPackages",
        "tags": [
          "Game packages"
        ],
        "summary": "Game packages of a project",
        "description": "For people who can edit the project. Lists every version with its upload progress, the storage your project uses and the limits.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Packages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "available": {
                      "type": "boolean",
                      "description": "false if game packages are switched off on this server"
                    },
                    "packages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "version": {
                            "type": "string"
                          },
                          "engine": {
                            "type": "string"
                          },
                          "status": {
                            "enum": [
                              "uploading",
                              "ready"
                            ],
                            "description": "`ready` once every file is uploaded and `finish` was called. Only ready versions reach players."
                          },
                          "size": {
                            "type": "integer",
                            "description": "Bytes of all files"
                          },
                          "received": {
                            "type": "integer",
                            "description": "Bytes uploaded so far"
                          },
                          "files": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "path": {
                                  "type": "string"
                                },
                                "size": {
                                  "type": "integer"
                                },
                                "sha256": {
                                  "type": "string"
                                },
                                "received": {
                                  "type": "integer",
                                  "description": "Bytes of this file uploaded so far"
                                },
                                "done": {
                                  "type": "boolean"
                                }
                              },
                              "required": [
                                "path",
                                "size",
                                "sha256",
                                "received",
                                "done"
                              ]
                            }
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64",
                            "description": "Unix time in milliseconds"
                          },
                          "readyAt": {
                            "oneOf": [
                              {
                                "type": "integer",
                                "format": "int64",
                                "description": "Unix time in milliseconds"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "name",
                          "version",
                          "engine",
                          "status",
                          "size",
                          "received",
                          "files",
                          "createdAt",
                          "readyAt"
                        ]
                      }
                    },
                    "usedBytes": {
                      "type": "integer"
                    },
                    "quotaBytes": {
                      "type": "integer"
                    },
                    "maxVersionBytes": {
                      "type": "integer"
                    },
                    "chunkBytes": {
                      "type": "integer",
                      "description": "Largest piece for one upload request"
                    },
                    "keepVersions": {
                      "type": "integer",
                      "description": "Ready versions kept per package; `finish` deletes older ones"
                    },
                    "license": {
                      "type": "object",
                      "properties": {
                        "version": {
                          "type": "string"
                        },
                        "text": {
                          "type": "string",
                          "description": "The statement you confirm with every upload"
                        }
                      },
                      "required": [
                        "version",
                        "text"
                      ]
                    }
                  },
                  "required": [
                    "available",
                    "packages",
                    "usedBytes",
                    "quotaBytes",
                    "maxVersionBytes",
                    "chunkBytes",
                    "keepVersions",
                    "license"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      },
      "post": {
        "operationId": "createPackageVersion",
        "tags": [
          "Game packages"
        ],
        "summary": "Start the upload of a package version",
        "description": "Send the manifest from `gampeon-package.json`, which the GAMPEON cook command writes, and confirm your license (`licenseConfirmed: true`, text in `GET …/packages`). Only content: the `.uplugin` must not have code modules, and only cooked paks (`Content/Paks/<platform>/*.pak|utoc|ucas|sig`) and files for web interfaces (`Nui/…`) are allowed. Sending the same manifest again continues an unfinished upload.\n\nNot available with access tokens.\n\nRate limit: 30 per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "201": {
            "description": "Version created (200: unfinished upload continued)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "engine": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "uploading",
                        "ready"
                      ],
                      "description": "`ready` once every file is uploaded and `finish` was called. Only ready versions reach players."
                    },
                    "size": {
                      "type": "integer",
                      "description": "Bytes of all files"
                    },
                    "received": {
                      "type": "integer",
                      "description": "Bytes uploaded so far"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "sha256": {
                            "type": "string"
                          },
                          "received": {
                            "type": "integer",
                            "description": "Bytes of this file uploaded so far"
                          },
                          "done": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "path",
                          "size",
                          "sha256",
                          "received",
                          "done"
                        ]
                      }
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "readyAt": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "name",
                    "version",
                    "engine",
                    "status",
                    "size",
                    "received",
                    "files",
                    "createdAt",
                    "readyAt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `invalid_field`, `invalid_package`, `invalid_package_file`, `license_not_confirmed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`, `project_hidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `version_exists`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `package_too_large`, `quota_exceeded`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 30 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `packages_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "30 per hour per account",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "manifest": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Package name, the name of the content plugin",
                        "pattern": "^[A-Za-z][A-Za-z0-9_]{1,47}$"
                      },
                      "version": {
                        "type": "string",
                        "description": "Version, such as `1.0.0`. Each upload needs a new version.",
                        "pattern": "^\\d{1,4}\\.\\d{1,4}\\.\\d{1,6}$"
                      },
                      "engine": {
                        "type": "string",
                        "description": "The GAMPEON version the package was cooked for",
                        "pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$"
                      },
                      "files": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "path": {
                              "type": "string",
                              "description": "Path inside the package, such as `GamCity.uplugin`, `Content/Paks/WindowsClient/GamCity….pak` or `Nui/icons/cash.png`"
                            },
                            "size": {
                              "type": "integer",
                              "description": "Bytes",
                              "minimum": 1
                            },
                            "sha256": {
                              "type": "string",
                              "description": "SHA-256, 64 hex characters"
                            }
                          },
                          "required": [
                            "path",
                            "size",
                            "sha256"
                          ]
                        },
                        "description": "All files of the package, 1 to 256",
                        "minItems": 1,
                        "maxItems": 256
                      }
                    },
                    "required": [
                      "name",
                      "version",
                      "engine",
                      "files"
                    ]
                  },
                  "licenseConfirmed": {
                    "const": true,
                    "description": "You confirm the license statement"
                  }
                },
                "required": [
                  "manifest",
                  "licenseConfirmed"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/projects/{slug}/packages/{name}/{version}/files/{sha256}": {
      "put": {
        "operationId": "uploadPackageChunk",
        "tags": [
          "Game packages"
        ],
        "summary": "Upload a piece of a package file",
        "description": "Send the file in pieces of at most `chunkBytes` (64 MB) in order. `offset` must equal the bytes the server has (`received`); after an interruption, read `GET …/packages` and continue there. With the last piece the server checks the SHA-256 and, for the `.uplugin`, that it has no code modules. Files that GAMPEON already has from an earlier version answer `done: true` right away.\n\nNot available with access tokens.\n\nRate limit: 2000 per hour per account.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Package name, as the Unreal plugin",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z][A-Za-z0-9_]{1,47}$"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Package version, such as `1.0.0`",
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,4}\\.\\d{1,4}\\.\\d{1,6}$"
            }
          },
          {
            "name": "sha256",
            "in": "path",
            "required": true,
            "description": "SHA-256 of the file, 64 lowercase hex characters",
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{64}$"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": true,
            "description": "Byte position of this piece in the file",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Piece stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "integer"
                    },
                    "size": {
                      "type": "integer"
                    },
                    "done": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "received",
                    "size",
                    "done"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error codes: `upload_aborted`, `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `offset_mismatch`, `version_ready`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Error codes: `length_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error codes: `chunk_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error codes: `checksum_mismatch`, `invalid_package`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 2000 per hour per account. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `packages_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "507": {
            "description": "Error codes: `storage_full`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user",
        "x-gampeon-rate-limit": "2000 per hour per account",
        "requestBody": {
          "required": true,
          "description": "The bytes of this piece",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      }
    },
    "/v1/projects/{slug}/packages/{name}/{version}/finish": {
      "post": {
        "operationId": "finishPackageVersion",
        "tags": [
          "Game packages"
        ],
        "summary": "Make a package version playable",
        "description": "Checks that every file is uploaded and marks the version `ready`. From then on, players of your project's servers that run this version get it when they press Play. Keeps the newest 3 ready versions of the package and deletes older ones.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Package name, as the Unreal plugin",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z][A-Za-z0-9_]{1,47}$"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Package version, such as `1.0.0`",
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,4}\\.\\d{1,4}\\.\\d{1,6}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Version is ready",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "engine": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "uploading",
                        "ready"
                      ],
                      "description": "`ready` once every file is uploaded and `finish` was called. Only ready versions reach players."
                    },
                    "size": {
                      "type": "integer",
                      "description": "Bytes of all files"
                    },
                    "received": {
                      "type": "integer",
                      "description": "Bytes uploaded so far"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "sha256": {
                            "type": "string"
                          },
                          "received": {
                            "type": "integer",
                            "description": "Bytes of this file uploaded so far"
                          },
                          "done": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "path",
                          "size",
                          "sha256",
                          "received",
                          "done"
                        ]
                      }
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "readyAt": {
                      "oneOf": [
                        {
                          "type": "integer",
                          "format": "int64",
                          "description": "Unix time in milliseconds"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "name",
                    "version",
                    "engine",
                    "status",
                    "size",
                    "received",
                    "files",
                    "createdAt",
                    "readyAt"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error codes: `upload_incomplete`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `packages_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/projects/{slug}/packages/{name}/{version}": {
      "delete": {
        "operationId": "deletePackageVersion",
        "tags": [
          "Game packages"
        ],
        "summary": "Delete a package version",
        "description": "Removes the version and every file no other version uses. Servers that still run it can no longer be joined until they load another ready version.\n\nNot available with access tokens.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Project slug",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Package name, as the Unreal plugin",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z][A-Za-z0-9_]{1,47}$"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Package version, such as `1.0.0`",
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,4}\\.\\d{1,4}\\.\\d{1,6}$"
            }
          }
        ],
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Error codes: `unauthorized`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error codes: `token_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "user"
      }
    },
    "/v1/packages/files/{sha256}": {
      "get": {
        "operationId": "downloadPackageFile",
        "tags": [
          "Game packages"
        ],
        "summary": "Download a file of a game package",
        "description": "For the launcher. Needs the `packageGrant` from `POST /v1/servers/{id}/join` in the header `x-package-grant` (60 minutes, only for the packages of that join). Supports `Range: bytes=start-` to continue an interrupted download. Check the SHA-256 after downloading.\n\nRate limit: 600 per hour per grant.",
        "parameters": [
          {
            "name": "sha256",
            "in": "path",
            "required": true,
            "description": "SHA-256 of the file, 64 lowercase hex characters",
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{64}$"
            }
          },
          {
            "name": "x-package-grant",
            "in": "header",
            "required": true,
            "description": "Download grant from the join",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "range",
            "in": "header",
            "required": false,
            "description": "Optional single range, `bytes=start-` or `bytes=start-end`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The file (206 for a range)",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Error codes: `invalid_package_grant`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error codes: `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "Error codes: `range_not_satisfiable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limit: 600 per hour per grant. Error codes: `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error codes: `packages_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-gampeon-auth": "none",
        "x-gampeon-rate-limit": "600 per hour per grant"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "session": {
        "type": "http",
        "scheme": "bearer",
        "description": "Session token from `POST /v1/sessions`."
      },
      "serverKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-server-key",
        "description": "Server key from `POST /v1/servers`. Only for heartbeats."
      },
      "accessToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "gpat_",
        "description": "Personal access token from the portal or `POST /v1/me/tokens`. Only for routes with `x-gampeon-token-scope`; the listed scope is required."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code, for example `not_found` or `rate_limited`."
          },
          "message": {
            "type": "string",
            "description": "Human-readable detail. Only present for some errors."
          }
        },
        "required": [
          "error"
        ]
      },
      "UserRef": {
        "type": "object",
        "properties": {
          "handle": {
            "type": "string",
            "description": "Unique handle",
            "pattern": "^[a-z0-9_]{3,24}$"
          },
          "displayName": {
            "type": "string"
          },
          "avatar": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "handle",
          "displayName"
        ]
      },
      "Me": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Account ID"
          },
          "email": {
            "type": "string",
            "description": "Email address. Only visible to the account itself with a portal session, never with an access token.",
            "format": "email"
          },
          "displayName": {
            "type": "string"
          },
          "handle": {
            "type": "string",
            "pattern": "^[a-z0-9_]{3,24}$"
          },
          "bio": {
            "type": "string"
          },
          "avatar": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "cover": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "user",
              "moderator",
              "admin"
            ]
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "emailVerified": {
            "type": "boolean"
          },
          "birthMonth": {
            "oneOf": [
              {
                "type": "string",
                "description": "`YYYY-MM`. `null` for accounts created before the age check; they should add it with `PATCH /v1/me`. Like the email address, only visible with a portal session, never with an access token."
              },
              {
                "type": "null"
              }
            ]
          },
          "country": {
            "oneOf": [
              {
                "type": "string",
                "description": "Country code (ISO 3166-1 alpha-2, for example `DE`). `null` when not set.",
                "pattern": "^[A-Z]{2}$"
              },
              {
                "type": "null"
              }
            ]
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileLink"
            },
            "description": "Profiles on other platforms, at most one per platform (up to 10)."
          },
          "ageGroup": {
            "type": "string",
            "enum": [
              "protected",
              "adult"
            ],
            "description": "`protected`: under 16 or birth month unknown. Stricter privacy defaults apply."
          },
          "terms": {
            "type": "object",
            "properties": {
              "version": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "Last accepted version of the Terms of Use and Community Guidelines, `null` if none yet"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "acceptedAt": {
                "oneOf": [
                  {
                    "type": "integer",
                    "format": "int64",
                    "description": "Unix time in milliseconds"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "currentVersion": {
                "type": "string",
                "description": "Version in force"
              },
              "current": {
                "type": "boolean",
                "description": "`false`: show the terms and ask the person to accept them (`POST /v1/me/terms`)"
              }
            },
            "required": [
              "version",
              "acceptedAt",
              "currentVersion",
              "current"
            ]
          },
          "notifications": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Notification"
            },
            "description": "The 50 newest notifications"
          }
        },
        "required": [
          "id",
          "displayName",
          "handle",
          "bio",
          "avatar",
          "cover",
          "role",
          "createdAt",
          "emailVerified",
          "country",
          "links",
          "terms",
          "notifications"
        ]
      },
      "Notification": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "description": "For example `achievement_unlocked`, `content_blocked`, `studio_invite`, `appeal_decided`, `page_hidden`, `token_created`."
          },
          "body": {
            "type": "string",
            "description": "JSON-encoded details. The shape depends on `kind`."
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          }
        },
        "required": [
          "kind",
          "body",
          "createdAt"
        ]
      },
      "Profile": {
        "type": "object",
        "properties": {
          "handle": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "bio": {
            "type": "string"
          },
          "avatar": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "cover": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "staff": {
            "type": "boolean",
            "description": "True for moderators and admins"
          },
          "joinedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "resources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResourceListing"
            },
            "description": "Public marketplace items (up to 100)"
          },
          "servers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Server"
            },
            "description": "Servers that are online right now"
          },
          "studios": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudioMembership"
            }
          },
          "projects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Project"
            },
            "description": "Own projects and projects of their studios (up to 100)"
          },
          "achievements": {
            "$ref": "#/components/schemas/AchievementShowcase"
          },
          "connections": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "steam",
                    "discord"
                  ]
                },
                "name": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "url": {
                  "type": "string",
                  "description": "Profile page at the provider",
                  "format": "uri"
                }
              },
              "required": [
                "provider",
                "name",
                "url"
              ]
            },
            "description": "Steam and Discord accounts the person chose to show. Always empty for accounts under 16."
          },
          "country": {
            "oneOf": [
              {
                "type": "string",
                "description": "Country the person says they are from (ISO 3166-1 alpha-2). Always `null` for accounts under 16.",
                "pattern": "^[A-Z]{2}$"
              },
              {
                "type": "null"
              }
            ]
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileLink"
            },
            "description": "Profiles on other platforms the person entered themselves (not verified). Always empty for accounts under 16."
          }
        },
        "required": [
          "handle",
          "displayName",
          "bio",
          "avatar",
          "cover",
          "staff",
          "joinedAt",
          "resources",
          "servers",
          "studios",
          "projects",
          "achievements",
          "connections",
          "country",
          "links"
        ]
      },
      "ProfileLink": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string",
            "enum": [
              "twitch",
              "kick",
              "youtube",
              "tiktok",
              "instagram",
              "x",
              "facebook",
              "reddit",
              "github",
              "bluesky"
            ]
          },
          "handle": {
            "type": "string",
            "description": "Name on the platform. YouTube uses `@name` or `channel/<id>`, Facebook a username or a numeric profile ID."
          },
          "url": {
            "type": "string",
            "description": "Profile page, built by GAMPEON from the name. Never a link the person entered.",
            "format": "uri"
          }
        },
        "required": [
          "platform",
          "handle",
          "url"
        ]
      },
      "Server": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Server ID",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "website": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "owner": {
            "$ref": "#/components/schemas/UserRef"
          },
          "online": {
            "type": "boolean",
            "description": "Sent a heartbeat in the last 90 seconds"
          },
          "address": {
            "oneOf": [
              {
                "type": "string",
                "description": "Host name or IP address players connect to"
              },
              {
                "type": "null"
              }
            ]
          },
          "port": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "players": {
            "type": "integer"
          },
          "maxPlayers": {
            "type": "integer"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "language": {
            "oneOf": [
              {
                "type": "string",
                "description": "Language code such as `en` or `de-DE`"
              },
              {
                "type": "null"
              }
            ]
          },
          "lastSeen": {
            "type": "integer",
            "format": "int64",
            "description": "Last heartbeat (Unix time in milliseconds)"
          },
          "peakPlayers": {
            "type": "integer",
            "description": "Highest player count reported over two consecutive heartbeats"
          },
          "project": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ProjectRef"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "website",
          "owner",
          "online",
          "address",
          "port",
          "players",
          "maxPlayers",
          "tags",
          "language",
          "lastSeen",
          "peakPlayers",
          "project"
        ]
      },
      "ProjectRef": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "required": [
          "slug",
          "title"
        ]
      },
      "ResourceListing": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Unique resource name",
            "pattern": "^[a-z0-9][a-z0-9_\\-]{1,47}$"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "script",
              "vehicle",
              "map",
              "prop",
              "clothing",
              "gamemode",
              "tool"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "owner": {
            "$ref": "#/components/schemas/UserRef"
          },
          "latestVersion": {
            "oneOf": [
              {
                "type": "string",
                "description": "Newest version that is not blocked"
              },
              {
                "type": "null"
              }
            ]
          },
          "image": {
            "oneOf": [
              {
                "type": "string",
                "description": "Path of the preview image, relative to the API, for example `/v1/images/<sha256>.webp`"
              },
              {
                "type": "null"
              }
            ]
          },
          "downloads": {
            "type": "integer",
            "description": "Downloads of all versions"
          },
          "price": {
            "type": "integer",
            "description": "Price in GAMPEONS, 0 = free"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          }
        },
        "required": [
          "name",
          "title",
          "description",
          "kind",
          "tags",
          "owner",
          "latestVersion",
          "image",
          "downloads",
          "price",
          "createdAt",
          "updatedAt"
        ]
      },
      "ResourceVersion": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "description": "Semantic version",
            "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.\\-]+)?$"
          },
          "sha256": {
            "type": "string",
            "description": "SHA-256 of the file (hex)"
          },
          "sizeBytes": {
            "type": "integer"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "downloads": {
            "type": "integer"
          },
          "hasFile": {
            "type": "boolean",
            "description": "The file has been uploaded"
          },
          "url": {
            "oneOf": [
              {
                "type": "string",
                "description": "Download path relative to the API, once the file is uploaded"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "version",
          "sha256",
          "sizeBytes",
          "createdAt",
          "downloads",
          "hasFile",
          "url"
        ]
      },
      "ResourceDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ResourceListing"
          },
          {
            "type": "object",
            "properties": {
              "owner": {
                "type": "string",
                "description": "Display name of the owner (kept for compatibility; see `ownerProfile`)"
              },
              "ownerProfile": {
                "$ref": "#/components/schemas/UserRef"
              },
              "project": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/ProjectRef"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "versions": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ResourceVersion"
                },
                "description": "Versions that are not blocked, newest first"
              },
              "requiresPurchase": {
                "type": "boolean",
                "description": "The item costs GAMPEONS"
              },
              "owned": {
                "type": "boolean",
                "description": "You may download it: free, your own, or bought. `false` without a session for paid items"
              }
            },
            "required": [
              "owner",
              "ownerProfile",
              "project",
              "versions",
              "requiresPurchase",
              "owned"
            ]
          }
        ]
      },
      "MyResource": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ResourceListing"
          },
          {
            "type": "object",
            "properties": {
              "versions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "sizeBytes": {
                      "type": "integer"
                    },
                    "createdAt": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix time in milliseconds"
                    },
                    "downloads": {
                      "type": "integer"
                    },
                    "blocked": {
                      "type": "boolean",
                      "description": "Blocked after a report (DSA Art. 17). Not delivered any more."
                    },
                    "hasFile": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "version",
                    "sizeBytes",
                    "createdAt",
                    "downloads",
                    "blocked",
                    "hasFile"
                  ]
                },
                "description": "All versions including blocked ones, newest first"
              }
            },
            "required": [
              "versions"
            ]
          }
        ]
      },
      "Studio": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
          },
          "name": {
            "type": "string"
          },
          "tagline": {
            "type": "string"
          },
          "logo": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "cover": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "members": {
            "type": "integer",
            "description": "Number of members"
          },
          "projects": {
            "type": "integer",
            "description": "Number of visible projects"
          },
          "players": {
            "type": "integer",
            "description": "Players on online servers of all projects"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          }
        },
        "required": [
          "slug",
          "name",
          "tagline",
          "logo",
          "cover",
          "members",
          "projects",
          "players",
          "createdAt"
        ]
      },
      "StudioMembership": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Studio"
          },
          {
            "type": "object",
            "properties": {
              "role": {
                "type": "string",
                "enum": [
                  "owner",
                  "admin",
                  "member"
                ]
              },
              "hidden": {
                "type": "boolean",
                "description": "Hidden by moderation (only in `/v1/me/studios`)"
              }
            },
            "required": [
              "role"
            ]
          }
        ]
      },
      "StudioMember": {
        "type": "object",
        "properties": {
          "handle": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "avatar": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "joinedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          }
        },
        "required": [
          "handle",
          "displayName",
          "avatar",
          "role",
          "joinedAt"
        ]
      },
      "StudioInvite": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "handle": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "member"
            ]
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "expiresAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          }
        },
        "required": [
          "id",
          "handle",
          "displayName",
          "role",
          "createdAt",
          "expiresAt"
        ]
      },
      "Hidden": {
        "type": "object",
        "properties": {
          "at": {
            "type": "integer",
            "format": "int64",
            "description": "When moderation hid the page"
          },
          "reason": {
            "oneOf": [
              {
                "type": "string",
                "description": "Statement of reasons (DSA Art. 17)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "at",
          "reason"
        ]
      },
      "Project": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"
          },
          "title": {
            "type": "string"
          },
          "tagline": {
            "type": "string"
          },
          "genre": {
            "type": "string",
            "enum": [
              "roleplay",
              "racing",
              "shooter",
              "survival",
              "parkour",
              "sandbox",
              "minigames",
              "simulation",
              "other"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "concept",
              "development",
              "alpha",
              "beta",
              "released"
            ],
            "description": "Development stage, in this order: concept, development, alpha, beta, released"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "en",
                "de",
                "fr",
                "es",
                "it",
                "pt",
                "nl",
                "pl",
                "cs",
                "sk",
                "hu",
                "ro",
                "bg",
                "el",
                "tr",
                "ru",
                "uk",
                "sv",
                "da",
                "no",
                "fi",
                "ar",
                "he",
                "hi",
                "th",
                "vi",
                "id",
                "ja",
                "ko",
                "zh"
              ]
            },
            "description": "Languages of the game (ISO 639-1), up to 10"
          },
          "cover": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "owner": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "type": {
                    "const": "studio"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "logo": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "type",
                  "slug",
                  "name"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "const": "user"
                  },
                  "handle": {
                    "type": "string"
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "avatar": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "type",
                  "handle",
                  "displayName"
                ]
              }
            ]
          },
          "players": {
            "type": "integer",
            "description": "Players on linked servers that are online"
          },
          "online": {
            "type": "integer",
            "description": "Linked servers that are online"
          },
          "items": {
            "type": "integer",
            "description": "Linked marketplace items with at least one free version"
          },
          "favorites": {
            "type": "integer",
            "description": "Accounts that marked the project as a favorite"
          },
          "views": {
            "type": "integer",
            "description": "Page views (counted once per visitor and day, the team not included)"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "releasedAt": {
            "oneOf": [
              {
                "type": "integer",
                "format": "int64",
                "description": "First time the status was set to `released`"
              },
              {
                "type": "null"
              }
            ]
          },
          "publishedAt": {
            "oneOf": [
              {
                "type": "integer",
                "format": "int64",
                "description": "When the project was published. `null`: draft, only the team sees it"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "slug",
          "title",
          "tagline",
          "genre",
          "tags",
          "status",
          "languages",
          "cover",
          "owner",
          "players",
          "online",
          "items",
          "favorites",
          "views",
          "createdAt",
          "updatedAt",
          "releasedAt",
          "publishedAt"
        ]
      },
      "ProjectLinks": {
        "type": "object",
        "properties": {
          "discord": {
            "type": "string"
          },
          "youtube": {
            "type": "string"
          },
          "x": {
            "type": "string"
          },
          "twitch": {
            "type": "string"
          },
          "tiktok": {
            "type": "string"
          },
          "steam": {
            "type": "string"
          }
        },
        "required": [],
        "description": "Links besides the website: Discord invite, YouTube channel, X, Twitch, TikTok profile, Steam store page. Only `https://`. In a change, an empty string removes a link."
      },
      "GalleryItem": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "const": "image"
              },
              "url": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "Image path (`/v1/media/…`). `null` only for the team when moderation removed the image"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "caption": {
                "type": "string"
              },
              "removed": {
                "type": "boolean",
                "description": "Only for the team: removed by moderation"
              }
            },
            "required": [
              "id",
              "type",
              "url",
              "width",
              "height",
              "caption"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "const": "video"
              },
              "provider": {
                "const": "youtube"
              },
              "videoId": {
                "type": "string",
                "description": "YouTube video ID (11 characters)"
              },
              "caption": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "type",
              "provider",
              "videoId",
              "caption"
            ]
          }
        ]
      },
      "ChecklistItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "tagline",
              "description",
              "cover",
              "gallery",
              "tags",
              "languages",
              "links"
            ]
          },
          "done": {
            "type": "boolean"
          },
          "required": {
            "type": "boolean",
            "description": "Needed to publish. Required: a tagline and an about text with at least 100 characters"
          }
        },
        "required": [
          "id",
          "done",
          "required"
        ]
      },
      "Achievement": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "start",
              "creator",
              "operator",
              "studios",
              "projects",
              "veteran",
              "community",
              "secret",
              "play"
            ]
          },
          "tier": {
            "type": "string",
            "enum": [
              "bronze",
              "silver",
              "gold",
              "platinum"
            ]
          },
          "points": {
            "type": "integer"
          },
          "title": {
            "type": "string",
            "description": "Hidden achievements show a placeholder until unlocked"
          },
          "description": {
            "type": "string"
          },
          "icon": {
            "type": "string",
            "description": "Icon name"
          },
          "source": {
            "type": "string",
            "enum": [
              "platform",
              "staff",
              "game"
            ],
            "description": "`platform`: from real numbers. `staff`: granted by the team. `game`: comes with the launcher."
          },
          "hidden": {
            "type": "boolean",
            "description": "Still hidden for this viewer"
          },
          "percent": {
            "type": "number",
            "description": "Share of all accounts that unlocked it, in percent"
          },
          "unlockedAt": {
            "oneOf": [
              {
                "type": "integer",
                "format": "int64",
                "description": "Unix time in milliseconds"
              },
              {
                "type": "null"
              }
            ]
          },
          "progress": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "value": {
                    "type": "integer"
                  },
                  "goal": {
                    "type": "integer"
                  }
                },
                "required": [
                  "value",
                  "goal"
                ]
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "category",
          "tier",
          "points",
          "title",
          "description",
          "icon",
          "source",
          "hidden",
          "percent",
          "unlockedAt",
          "progress"
        ]
      },
      "AchievementSummary": {
        "type": "object",
        "properties": {
          "unlocked": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "points": {
            "type": "integer"
          },
          "maxPoints": {
            "type": "integer"
          }
        },
        "required": [
          "unlocked",
          "total",
          "points",
          "maxPoints"
        ]
      },
      "AchievementShowcase": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AchievementSummary"
          },
          {
            "type": "object",
            "properties": {
              "showcase": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Achievement"
                },
                "description": "The 6 rarest unlocked achievements (without `progress`)"
              }
            },
            "required": [
              "showcase"
            ]
          }
        ]
      },
      "AchievementCategory": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "title"
        ]
      },
      "SocialTarget": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "user",
              "studio",
              "project"
            ]
          },
          "key": {
            "type": "string",
            "description": "Handle or slug"
          },
          "name": {
            "type": "string"
          },
          "avatar": {
            "oneOf": [
              {
                "type": "string",
                "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
              },
              {
                "type": "null"
              }
            ]
          },
          "since": {
            "type": "integer",
            "format": "int64",
            "description": "When the follow started"
          }
        },
        "required": [
          "type",
          "key",
          "name",
          "avatar",
          "since"
        ]
      },
      "Reactions": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "All reactions together"
          },
          "top": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "like",
                "love",
                "haha",
                "wow",
                "sad",
                "angry"
              ]
            },
            "description": "Up to three most frequent reactions, most frequent first (for the icons)"
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Number per reaction, only reactions that occur"
          }
        },
        "required": [
          "total",
          "top",
          "counts"
        ]
      },
      "LinkCard": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "domain": {
            "type": "string",
            "description": "Host without www."
          },
          "title": {
            "oneOf": [
              {
                "type": "string",
                "description": "Page title (og:title or <title>), up to 200 characters. `null` until the preview has been fetched"
              },
              {
                "type": "null"
              }
            ]
          },
          "siteName": {
            "oneOf": [
              {
                "type": "string",
                "description": "og:site_name"
              },
              {
                "type": "null"
              }
            ]
          },
          "youtube": {
            "oneOf": [
              {
                "type": "string",
                "description": "YouTube video ID, for a click-to-play embed"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "url",
          "domain",
          "title",
          "siteName",
          "youtube"
        ]
      },
      "PostImage": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Image path relative to the API (`/v1/media/…`)"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          }
        },
        "required": [
          "url",
          "width",
          "height"
        ]
      },
      "Gif": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "GIF ID at KLIPY"
          },
          "title": {
            "type": "string",
            "description": "Description from KLIPY, for alt text"
          },
          "url": {
            "type": "string",
            "description": "Image path relative to the API (`/v1/gifs/…/md`)"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "provider": {
            "type": "string",
            "description": "`klipy`. Show “via KLIPY” on the GIF"
          }
        },
        "required": [
          "id",
          "title",
          "url",
          "width",
          "height",
          "provider"
        ]
      },
      "GifResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "GIF ID, send it as `gif` with a post or comment"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "`/v1/gifs/…/md`"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "preview": {
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "description": "`/v1/gifs/…/sm`, for the picker"
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              }
            },
            "required": [
              "url",
              "width",
              "height"
            ]
          }
        },
        "required": [
          "id",
          "title",
          "url",
          "width",
          "height",
          "preview"
        ]
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "postId": {
            "type": "integer"
          },
          "parentId": {
            "oneOf": [
              {
                "type": "integer",
                "description": "Top-level comment this replies to"
              },
              {
                "type": "null"
              }
            ]
          },
          "body": {
            "type": "string",
            "description": "Plain text, up to 1,000 characters. Can be empty if there is a GIF"
          },
          "gif": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Gif"
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "editedAt": {
            "oneOf": [
              {
                "type": "integer",
                "format": "int64",
                "description": "Unix time in milliseconds"
              },
              {
                "type": "null"
              }
            ]
          },
          "author": {
            "$ref": "#/components/schemas/UserRef"
          },
          "mentions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Mentioned handles that exist (`@handle` in the text)"
          },
          "reactions": {
            "$ref": "#/components/schemas/Reactions"
          },
          "myReaction": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "like",
                  "love",
                  "haha",
                  "wow",
                  "sad",
                  "angry"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "replyCount": {
            "type": "integer",
            "description": "Replies (top-level comments only)"
          },
          "canEdit": {
            "type": "boolean"
          },
          "canDelete": {
            "type": "boolean",
            "description": "You wrote it or manage the post"
          },
          "removed": {
            "type": "object",
            "properties": {
              "at": {
                "type": "integer",
                "format": "int64",
                "description": "Unix time in milliseconds"
              },
              "reason": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "Statement of reasons"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "at",
              "reason"
            ]
          }
        },
        "required": [
          "id",
          "postId",
          "parentId",
          "body",
          "gif",
          "createdAt",
          "editedAt",
          "author",
          "mentions",
          "reactions",
          "myReaction",
          "replyCount",
          "canEdit",
          "canDelete"
        ]
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "body": {
            "type": "string",
            "description": "Plain text, up to 1,000 characters. Can be empty for photo and GIF posts and shares."
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix time in milliseconds"
          },
          "editedAt": {
            "oneOf": [
              {
                "type": "integer",
                "format": "int64",
                "description": "When the text was last changed"
              },
              {
                "type": "null"
              }
            ]
          },
          "author": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "type": {
                    "const": "user"
                  },
                  "handle": {
                    "type": "string"
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "avatar": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "type",
                  "handle",
                  "displayName",
                  "avatar"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "const": "studio"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "avatar": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "type",
                  "slug",
                  "name",
                  "avatar"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "const": "project"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "avatar": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Image path relative to the API (`/v1/media/…`). `null` without an image."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "type",
                  "slug",
                  "name",
                  "avatar"
                ]
              }
            ]
          },
          "postedBy": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/UserRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "audience": {
            "type": "string",
            "enum": [
              "public",
              "friends",
              "private"
            ],
            "description": "Who can see it. Studio and game posts are always public"
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostImage"
            },
            "description": "Up to 4 photos"
          },
          "gif": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Gif"
              },
              {
                "type": "null"
              }
            ]
          },
          "link": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LinkCard"
              },
              {
                "type": "null"
              }
            ]
          },
          "mentions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Mentioned handles that exist"
          },
          "shared": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "boolean",
                    "description": "false if the original was deleted, removed or isn’t visible to you"
                  },
                  "post": {
                    "description": "The original post (without comments and permissions)",
                    "type": "object"
                  }
                },
                "required": [
                  "available"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "commentMode": {
            "type": "string",
            "enum": [
              "everyone",
              "friends",
              "off"
            ],
            "description": "Who can comment, as it applies (under 16: at most friends)"
          },
          "pinned": {
            "type": "boolean",
            "description": "Pinned to the top of the timeline"
          },
          "reactions": {
            "$ref": "#/components/schemas/Reactions"
          },
          "myReaction": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "like",
                  "love",
                  "haha",
                  "wow",
                  "sad",
                  "angry"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "likes": {
            "type": "integer",
            "description": "All reactions (older field)"
          },
          "liked": {
            "type": "boolean",
            "description": "Whether you reacted (older field)"
          },
          "commentCount": {
            "type": "integer"
          },
          "shareCount": {
            "type": "integer"
          },
          "latestComments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Comment"
            },
            "description": "The two newest top-level comments, oldest first"
          },
          "canEdit": {
            "type": "boolean"
          },
          "canDelete": {
            "type": "boolean",
            "description": "Whether you may delete it"
          },
          "canPin": {
            "type": "boolean"
          },
          "canComment": {
            "type": "boolean"
          },
          "canShare": {
            "type": "boolean"
          },
          "removed": {
            "type": "object",
            "properties": {
              "at": {
                "type": "integer",
                "format": "int64",
                "description": "Unix time in milliseconds"
              },
              "reason": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "Statement of reasons"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "at",
              "reason"
            ]
          }
        },
        "required": [
          "id",
          "body",
          "createdAt",
          "editedAt",
          "author",
          "postedBy",
          "audience",
          "images",
          "gif",
          "link",
          "mentions",
          "reactions",
          "myReaction",
          "likes",
          "liked",
          "canDelete"
        ]
      },
      "FeedItem": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "kind": {
                "const": "post"
              },
              "at": {
                "type": "integer",
                "format": "int64",
                "description": "Unix time in milliseconds"
              },
              "post": {
                "$ref": "#/components/schemas/Post"
              }
            },
            "required": [
              "kind",
              "at",
              "post"
            ]
          },
          {
            "type": "object",
            "properties": {
              "kind": {
                "const": "release"
              },
              "at": {
                "type": "integer",
                "format": "int64",
                "description": "Unix time in milliseconds"
              },
              "release": {
                "type": "object",
                "properties": {
                  "resource": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      },
                      "kind": {
                        "type": "string"
                      },
                      "image": {
                        "oneOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "name",
                      "title",
                      "kind",
                      "image"
                    ]
                  },
                  "version": {
                    "type": "string"
                  },
                  "owner": {
                    "$ref": "#/components/schemas/UserRef"
                  },
                  "project": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/ProjectRef"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "resource",
                  "version",
                  "owner",
                  "project"
                ]
              }
            },
            "required": [
              "kind",
              "at",
              "release"
            ]
          }
        ]
      }
    }
  }
}
