{
  "key": "gamersites",
  "name": "Gamer Sites",
  "title": "Gamer Sites API documentation",
  "revision": "82cf65879b5fa09800bc0d4fc5e31557332cabbe",
  "eyebrow": "Gamer Sites / Developer reference",
  "sections": [
    {
      "id": "start",
      "title": "Start here",
      "html": "<p>The public API origin used by the existing Aero application is <code>https://aero.gamersites.gg</code>. These documentation pages are served by <code>gamersites.gg</code>; they do not proxy API requests or share a session with the API origin.</p><p>Gamer Sites exposes a Discord-backed community API used by Aero and Command Center. The community session API, WordPress integration credential, outbound bot agent and private MCP connection are separate trust boundaries. Choose the interface that already matches your approved integration.</p><p>These pages describe existing interfaces. Reading documentation does not create an account, issue a credential, grant a permission, or authorize a write. Examples contain synthetic identifiers and reserved example domains. Obtain the approved API and identity origins from your existing project configuration.</p><ul><li>Browser integrations use the existing Gamer Sites session on the configured API origin.</li><li>Read /api/v1/me, then choose a community from its current memberships.</li><li>Read Aero state and version before editing. The bot independently checks current Discord guild authority.</li><li>No public “try it” controls are provided; saves, publishes and tests can have real effects.</li></ul><p>Agents must use their integration's existing approved identity and permissions. This reference provides context, not a shared API account, a credential, or permission to reuse someone else's session.</p>"
    },
    {
      "id": "authentication",
      "title": "Authentication",
      "html": "<p>Community routes use the secure, HttpOnly <code>__Host-gs_session</code> cookie established by the existing Discord sign-in flow. A Discord access token or a generic Bearer token is not a replacement. The session is created with a one-day browser max age. Start normal sign-in from the application; it performs state and browser binding checks.</p><p>Every session mutation requires the exact configured API <code>Origin</code> and <code>X-CSRF-Token</code> matching the session. Read that CSRF value from <code>GET /api/v1/me</code> inside the existing signed-in app; keep it in memory. Do not copy cookies into examples, expose the token in a public page, or bypass Origin checks.</p><p>The site integration routes use the existing operator-provisioned WordPress credential. The agent websocket uses its own credential and outbound connection. Keep both on their existing private server/runtime boundaries. Private MCP OAuth uses its own resource/client/scope and an explicit room grant; a community session does not automatically grant MCP access.</p><p>There is no generic bearer-token substitute documented for community-session routes. An integration that cannot use the approved session flow needs an operator-reviewed integration contract; this documentation does not create one. Do not automate sign-in or copy another user's session to work around a missing grant.</p>"
    },
    {
      "id": "permissions",
      "title": "Permissions and communities",
      "html": "<div class=\"api-table\"><table><thead><tr><th scope=\"col\">Capability</th><th scope=\"col\">Boundary</th></tr></thead><tbody><tr><td>community.aero.view</td><td>Read Aero state in the selected community.</td></tr><tr><td>community.aero.manage</td><td>Write Aero state; view is also required. Current Discord management authority and verified binding are rechecked.</td></tr><tr><td>community.upgrades.view / manage</td><td>Supporter/upgrades reads or management; not interchangeable with billing control.</td></tr><tr><td>community.billing.manage / community.stripe.manage</td><td>Financial controls require their own grants.</td></tr><tr><td>community.admins.manage</td><td>Community administrators and invitation management.</td></tr><tr><td>Private chat / room permissions</td><td>Separate membership and per-room policy; Aero access does not create chat access.</td></tr></tbody></table></div><p>The <code>gamer_sites_admin</code> role’s seeded community grants are upgrades.view, upgrades.manage and admins.manage. It does not automatically include Aero, Stripe or billing privileges. Treat the server’s effective membership permissions as authoritative. The <code>memberships[].permissions</code> field returned by /me is a JSON-encoded string of permission names in this version; parse it carefully.</p>"
    },
    {
      "id": "reference",
      "title": "Endpoint reference",
      "html": "<div class=\"api-table\"><table><thead><tr><th scope=\"col\">Method</th><th scope=\"col\">Path</th><th scope=\"col\">Contract / permission</th></tr></thead><tbody><tr><td>GET</td><td><code>/api/v1/me</code></td><td>Existing GS session; identity, CSRF token, memberships, profile.</td></tr><tr><td>POST</td><td><code>/api/v1/me/preferences</code></td><td>Existing session + Origin/CSRF; {marketing_opt_in:boolean}.</td></tr><tr><td>POST</td><td><code>/api/v1/logout</code></td><td>Existing session + Origin/CSRF; {logged_out:true}.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/overview</code></td><td>Session; any upgrades.view, aero.view or chat.view capability for this community.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/aero</code></td><td>community.aero.view; verified Discord identity and guild management.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/settings</code></td><td>community.aero.view AND community.aero.manage; Origin/CSRF; settings patch.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/aero/welcome</code></td><td>Aero read boundary; welcome-card config, presets and capabilities.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/welcome/save</code></td><td>Aero write boundary; {expected_version,config}.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/welcome/preview</code></td><td>Aero write boundary; {expected_version,config,preview}; returns a bounded sample PNG preview.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/aero/rules</code></td><td>Same Aero read boundary; draft/config, version, channels.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/rules/settings</code></td><td>Aero write boundary; {expected_version,config}.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/rules/publish</code></td><td>Aero write boundary; {expected_version,dry_run:boolean}; may create/edit Discord content.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/aero/live</code></td><td>Aero read boundary; Going Live config and connection readiness.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/live/save</code></td><td>Aero write boundary; {expected_version,config}.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/live/check</code></td><td>Aero write boundary; {expected_version,creator_id}; checks provider state.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/live/test</code></td><td>Aero write boundary; {expected_version,creator_id,request_id}; delivers a test.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/aero/logging</code></td><td>Aero read boundary; config, catalog, capabilities, version and choices.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/logging/save</code></td><td>Aero write boundary; complete {expected_version,config}.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/logging/test</code></td><td>Aero write boundary; {expected_version,action_id,request_id}; sends a real test card.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/aero/temp_voice</code></td><td>Aero read boundary; temporary voice config, capabilities and version.</td></tr><tr><td>POST</td><td><code>/api/v1/communities/{community}/aero/temp_voice/save</code></td><td>Aero write boundary; complete {expected_version,config}.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/upgrade</code></td><td>Public upgrade presentation; active billing/configuration requirements apply.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/admins</code></td><td>community.admins.manage.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/payments</code></td><td>community.upgrades.view; Stripe-sensitive account details need stripe.manage.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/roles</code></td><td>community.upgrades.manage.</td></tr><tr><td>GET</td><td><code>/api/v1/communities/{community}/supporters</code></td><td>community.upgrades.view.</td></tr></tbody></table></div><p>Success is HTTP 200 in these handlers. <code>{community}</code> is the platform community ID obtained from the authorized membership, not the Discord guild ID. Discord IDs are represented as strings to preserve precision. The reference lists implemented source routes; runtime capability and release checks still apply.</p>"
    },
    {
      "id": "schemas",
      "title": "Aero schemas and examples",
      "html": "<p>Community overview response:</p><pre><code>{\n  &quot;id&quot;: &quot;22222222-2222-4222-8222-222222222222&quot;,\n  &quot;name&quot;: &quot;Example Community&quot;,\n  &quot;site_url&quot;: &quot;https://community.example.com&quot;,\n  &quot;guild_id&quot;: &quot;100000000000000001&quot;\n}</code></pre><p>Aero settings save accepts exactly <code>expected_version</code> (integer 0–9007199254740991) and a nonempty <code>settings</code> patch. Unknown/protected settings are rejected. Example request, after reading the current version:</p><pre><code>{\n  &quot;expected_version&quot;: 7,\n  &quot;settings&quot;: {\n    &quot;welcome_enabled&quot;: false,\n    &quot;welcome_title&quot;: &quot;Welcome to Example Community&quot;,\n    &quot;welcome_mode&quot;: &quot;embed&quot;,\n    &quot;welcome_accent_color&quot;: &quot;#3498db&quot;\n  }\n}</code></pre><p>Editable settings include welcome/gamer-sites booleans, channel selections, welcome text/image/link fields, up to 20 game_admin_role_ids and per-game status/sticky options. Sticky delay is an integer from 5 to 300 seconds. Text limits: welcome title 256, message 4096, footer and URLs 2048. Nonempty URLs require HTTPS without credentials. Welcome mode is embed or image; color is six hexadecimal digits. Use current returned channel and role choices; empty channel values clear a selection.</p><p>A successful read/save returns <code>community,readiness,settings,version,channels,roles,can_edit,outcome</code>. Read outcome is loaded; save outcome is saved and version must advance. Readiness separates API reachability, relay connection, Discord readiness and guild connection. A 503 may return the same envelope with null settings/version and an explicit code; never turn it into a successful empty state.</p><p>Module routes use full validated <code>config</code> rather than the base settings patch. Their exact validators live in <code>aero_shared/logging.py</code>, <code>temp_voice.py</code>, <code>live.py</code>, <code>welcome_cards.py</code> and <code>rules.py</code>. Read and preserve the current config, its version and capabilities. Do not fabricate a config from the short envelope examples. Logging test and live test request IDs are lowercase UUIDv4 strings.</p>"
    },
    {
      "id": "quick-start",
      "title": "Read-only integration quick start",
      "html": "<p>Run inside the existing signed-in app on its configured API origin. This example does not sign in, grant access or write anything:</p><pre><code>const response = await fetch('/api/v1/me', { credentials: 'same-origin' });\nconst account = await response.json();\nif (!response.ok) throw new Error(account.message || 'Sign in required');\nconst membership = account.memberships.find(row =&gt;\n  JSON.parse(row.permissions).includes('community.aero.view'));\nif (!membership) throw new Error('No approved Aero community');\nconst stateResponse = await fetch(\n  '/api/v1/communities/' + encodeURIComponent(membership.community_id) + '/aero',\n  { credentials: 'same-origin' }\n);\nconst state = await stateResponse.json();\nif (!stateResponse.ok) throw new Error(state.code || state.message || 'Aero unavailable');</code></pre><p>For an explicitly authorized settings edit, load the current state, preserve its version, send application/json plus the in-memory CSRF token from /me, then read back. Browser-origin handling is performed by the existing same-origin application. Do not try to set the forbidden Origin header from page JavaScript or send cookies from the public docs origin.</p>"
    },
    {
      "id": "errors",
      "title": "Errors, concurrency and delivery",
      "html": "<p>Typical failures use <code>{message:string}</code>. Base Aero operations can return a structured readiness envelope with <code>code</code> and <code>outcome</code>. Do not assume all errors share one schema.</p><div class=\"api-table\"><table><thead><tr><th scope=\"col\">Status</th><th scope=\"col\">Meaning / next action</th></tr></thead><tbody><tr><td>400</td><td>Malformed body, unknown settings, invalid IDs or unsupported value. Correct it.</td></tr><tr><td>401</td><td>Existing session absent/expired. Return to normal Discord sign-in.</td></tr><tr><td>403</td><td>Session CSRF/Origin, community capability or current guild authority denied. Do not expand access automatically.</td></tr><tr><td>404</td><td>Community/route is unknown or the optional feature is not mounted.</td></tr><tr><td>409</td><td>Stale version, changed binding or incompatible state. Reload and reconcile.</td></tr><tr><td>429</td><td>Rate limited on guarded endpoints; honor Retry-After when supplied.</td></tr><tr><td>503</td><td>Relay, Discord/provider or response contract unavailable. A write or delivery may already have occurred.</td></tr></tbody></table></div><p>Base Aero codes include <code>SETTINGS_VERSION_CONFLICT</code>, <code>AERO_RELAY_OFFLINE</code>, <code>AERO_RELAY_UNAVAILABLE</code>, <code>AERO_GUILD_UNAVAILABLE</code>, <code>AERO_DISCORD_NOT_READY</code> and <code>AERO_AGENT_CONTRACT_UNAVAILABLE</code>. Module errors may instead return a message.</p><p>A save requires the latest expected_version. After uncertain writes, read back. Logging/live tests can post real Discord content even while automation is paused. Preserve the test request_id and inspect its durable receipt before another attempt; never generate a new request ID merely to retry an uncertain send. Rules dry_run must be an explicit boolean; false can publish.</p>"
    },
    {
      "id": "other-interfaces",
      "title": "Other implemented interfaces",
      "html": "<p>Additional source contracts cover community checkout, billing portal, Stripe Connect, configuration, invitations, subscription actions, community chat and private team rooms. These remain scoped application/operator interfaces; publishing this page does not enable them or grant their permissions. Use their source validators and the existing UI before building a new authorized client.</p><p>WordPress-bound routes include <code>/v1/oauth/start</code>, <code>/v1/oauth/claim</code>, <code>/v1/dashboard</code> and <code>/v1/disconnect</code>; they require the existing site integration credential plus verified site/user/connection bindings. They are not anonymous setup endpoints. The bot relay is <code>/v1/agent</code> and is not a general code-execution interface.</p><p>The anonymous player-count feed is a specific allowlisted community aggregate implemented at <code>/api/v1/public/communities/homies/player-counts</code>, conditionally mounted by configuration. Do not generalize it to arbitrary community IDs. Unavailable/stale readings are distinct from zero players. Private desktop downloads require a GS session and an existing approved entitlement; filenames are allowlisted.</p><p>Optional MCP routes use resource <code>/mcp/gamer-sites</code> with <code>gs.chat.read</code>, <code>gs.chat.reply</code> and <code>gs.chat.events</code>. Its OAuth provider and room connection are opt-in and owner/room-bound. Do not present this as an open multi-community agent platform or infer an active grant from discovery availability.</p>"
    },
    {
      "id": "status",
      "title": "Implementation and deployment status",
      "html": "<p>Reviewed source: <code>82cf65879b5fa09800bc0d4fc5e31557332cabbe</code>, captured 24 October 2026. The existing API application uses the gs-platform-api branch. The release owner previously verified logging with this API contract and bot a582a75. A newer API release may be running; this reference is explicitly pinned to reviewed source rather than claiming byte-for-byte running-image identity. No authenticated live acceptance or mutation was performed.</p><div class=\"api-table\"><table><thead><tr><th scope=\"col\">Area</th><th scope=\"col\">Status</th></tr></thead><tbody><tr><td>Identity and Aero community settings</td><td>Implemented in pinned source. Deployment health alone does not prove per-user/guild readiness.</td></tr><tr><td>Rules, Going Live, logging, temporary voice</td><td>Implemented source routes; require matching bot validators and runtime capabilities.</td></tr><tr><td>Logging compatibility</td><td>The release owner previously verified this API contract with bot a582a75. The reviewed API no longer requires the removed capability field. This documentation task independently verified source, not the live bot settings. A post-action 503 still requires state/receipt inspection.</td></tr><tr><td>Private chat / MCP / player counts</td><td>Conditional mounting and explicit grants/configuration; not generally available merely because source exists.</td></tr><tr><td>OpenAPI</td><td>Curated community identity/overview/base Aero settings subset; module config contracts remain source-referenced.</td></tr><tr><td>Documentation website</td><td>Prepared for the existing live Gamer Sites root documentation section. Website publication does not change or deploy the API. No new site or tenant-site import is involved.</td></tr></tbody></table></div><p>Source authority: <code>aero_api/server.py</code>, <code>gamer_sites/identity.py</code>, <code>aero.py</code>, <code>community_modules.py</code>, <code>rules.py</code>, <code>going_live.py</code>, <code>store.py</code> and <code>aero_shared</code> validators. Recheck API and bot revisions together before using module writes.</p><p>Assistant delivery is not implemented as a general autonomous assistant service in the reviewed application. Optional private-room/MCP adapters have additional mounting, owner/room and grant boundaries. Discovery or a public documentation page is not proof that a particular account has access.</p>"
    },
    {
      "id": "agents",
      "title": "Reference for agents",
      "html": "<p>Use the native page as the human reference and the downloadable OpenAPI, concise agent context and full reference JSON as machine-readable context. The OpenAPI is a curated core subset; full module configurations remain grounded in the source validators named above. These files contain no access material or public action runner.</p>"
    }
  ],
  "publication": {
    "target": "https://gamersites.gg/developers/",
    "status": "Prepared for authorized production import; publication and live QA are separate.",
    "api_origin": "https://aero.gamersites.gg",
    "site": "Existing WordPress network root only; no tenant sites."
  }
}