Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/VasquezRivero92/Discord_Faceit/llms.txt

Use this file to discover all available pages before exploring further.

The Lobby API powers the Mixton Arena competitive team-draft system. Admins configure teams and captains; captains log in via Discord OAuth2 and use this API to pick players during the draft phase. All REST endpoints are mounted under /api/lobby and protected by the verifyUserToken middleware, which validates the same JWT cookie issued by the Auth API — but does not require an admin role, allowing assigned captains to authenticate.
Admin-only endpoints in this section additionally apply a verifyGuildAdmin middleware check. Captains can only call endpoints documented as Draft Action endpoints when it is their turn.

Authentication

Every request must carry the auth_token or admin_token cookie. Missing cookies return 401. Role scoping follows the same rules as the Admin API: admin users are restricted to their own guild, while global_admin users can target any guild via ?guildId=, x-guild-id header, or body guildId.

State

GET /api/lobby/me

Returns the current authenticated user’s decoded JWT payload. Response
{
  "user": {
    "userId": "123456789012345678",
    "username": "captainuser",
    "avatar": "a_deadbeef1234",
    "isAdmin": false,
    "role": "usuario",
    "servidor": null
  }
}

GET /api/lobby/users

Returns all verified users from Firestore enriched with Discord display details (displayName, avatarURL). Useful for populating player-selection dropdowns in the lobby UI. Response
{
  "users": [
    {
      "discordId": "123456789012345678",
      "faceit_player_id": "abcdef01-...",
      "faceit_nickname": "PlayerOne",
      "faceit_level": 8,
      "faceit_elo": 1850,
      "discordUser": {
        "username": "playerone",
        "displayName": "PlayerOne",
        "avatarURL": "https://cdn.discordapp.com/avatars/.../a_abc123.png"
      }
    }
  ]
}

GET /api/lobby/mixton-state

Returns the full, real-time state of the lobby for the resolved guild. This is the primary data source for the lobby UI. Response
{
  "state": {
    "teams": [
      {
        "id": "team_1",
        "name": "Equipo 1",
        "captain": {
          "discordId": "111111111111111111",
          "faceit_nickname": "CaptainOne",
          "faceit_level": 10,
          "faceit_elo": 3100,
          "discordUser": { "displayName": "CaptainOne", "avatarURL": "..." }
        },
        "players": []
      },
      {
        "id": "team_2",
        "name": "Equipo 2",
        "captain": null,
        "players": []
      }
    ],
    "pickOrder": ["team_1", "team_2"],
    "currentPickIndex": 0,
    "draftStarted": false,
    "draftFinished": false
  },
  "availablePlayers": {
    "S": [{ "discordId": "...", "faceit_nickname": "TopPlayer", "faceit_level": 10 }],
    "A": [],
    "B": [],
    "C": []
  },
  "commandEnabled": true,
  "guildId": "987654321098765432",
  "guildName": "Mixton Arena",
  "currentUser": {
    "userId": "123456789012345678",
    "username": "captainuser",
    "isAdmin": false
  }
}
availablePlayers
object
Players who have run /mixton_listo and are not yet assigned to any team. Keyed by ELO tier: S (Level 10), A (Levels 8–9), B (Levels 5–7), C (Levels 1–4). Entries within each tier are sorted by ELO descending. Tier can be overridden per-player by an admin.
commandEnabled
boolean
Whether the /mixton_listo and /mixton_no_listo Discord commands are currently enabled for this guild.

Admin Setup

All endpoints in this section require admin or global_admin role.

POST /api/lobby/mixton-setup

Configures the number of teams for the lobby. Resets all teams, pick order, captain assignments, and draft state. Request Body
numTeams
number
required
Number of teams to create. Must be between 2 and 6.
Response
{ "success": true, "state": { "...": "updated lobby state" } }

POST /api/lobby/mixton-assign-captain

Assigns a verified user as the captain of a specific team. Captains must be Tier S or Tier A (Level 8+, or with an S/A tier override). Automatically sends a DM to the assigned captain with a login link. Request Body
teamId
string
required
Team ID (e.g. "team_1").
discordId
string
required
Discord user ID of the player to assign as captain. Pass null or omit to clear the captain.
Response
{ "success": true, "state": { "...": "updated lobby state" } }
Returns 400 if the player’s tier is not S or A.

POST /api/lobby/mixton-pick-order

Sets the captain draft pick order. Every team ID in the array must have a captain assigned. Request Body
pickOrder
array
required
Ordered array of team IDs. Example: ["team_2", "team_1", "team_2", "team_1"].
Response
{ "success": true, "state": { "...": "updated lobby state" } }

POST /api/lobby/mixton-start

Starts the draft phase. Requires all teams to have captains assigned. Clears any previously picked players and sets draftStarted: true. Response
{ "success": true, "state": { "...": "updated lobby state" } }
Returns 400 if fewer than 2 teams exist or any team lacks a captain.

POST /api/lobby/mixton-create-match

Converts two fully-assembled lobby teams into a live Discord match. Both teams must be complete — each needs a captain and exactly 4 picked players (5 total). Creates two locked voice channels, moves members, posts a match announcement embed with result-reporting buttons, sends individual DMs to each player, and saves the match to faceit_active_matches. After this call, the lobby can be reset for the next session. Request Body
teamIdA
string
required
Team ID of the first team (e.g. "team_1").
teamIdB
string
required
Team ID of the second team (e.g. "team_2").
Response
{ "success": true, "matchId": "mixton_1700000000000_ab1cd2" }
Returns 400 if either team is incomplete (missing captain or fewer than 4 picked players), or 404 if the team IDs are not found in the current lobby state.

POST /api/lobby/mixton-reset

Resets the entire lobby to its default state (2 empty teams, no pick order, draft not started). Response
{ "success": true, "state": { "...": "default lobby state" } }

POST /api/lobby/mixton-load-team

Bulk-loads a full roster (captain + players) into a team. Resolves Firestore profiles for each player and removes them from any other team they may already occupy. Useful for importing pre-arranged teams. Request Body
teamId
string
required
Target team ID.
captainDiscordId
string
required
Discord ID (or Faceit UUID) of the captain.
playerDiscordIds
array
required
Array of Discord IDs (or Faceit UUIDs) of players to assign to the team.
Response
{ "success": true, "state": { "...": "updated lobby state" } }

POST /api/lobby/mixton-clear-team

Removes the captain and all players from the specified team’s roster. Request Body
teamId
string
required
Team ID to clear.
Response
{ "success": true, "state": { "...": "updated lobby state" } }

POST /api/lobby/mixton-reset-players

Marks all players who ran /mixton_listo as no longer ready, clearing the available player queue. Response
{ "success": true, "count": 12 }
count
number
Number of players whose ready status was reset.

POST /api/lobby/mixton-remove-player

This route name is registered twice in the router with different parameter shapes, serving two distinct purposes depending on which fields are sent in the request body. Usage 1 — Remove from ready queue Removes a specific player from the /mixton_listo ready queue. The player will no longer appear in the available player list until they run /mixton_listo again.
discordId
string
required
Discord user ID of the player to remove from the queue. Do not include teamId.
{ "success": true }

Usage 2 — Remove from a team roster Removes a player from a specific team’s players array without affecting their ready status.
teamId
string
required
Team ID to remove the player from (e.g. "team_1").
discordId
string
required
Discord user ID of the player to remove from the team.
{ "success": true, "state": { "...": "updated lobby state" } }
Returns 404 if the team ID is not found.

POST /api/lobby/mixton-toggle-command

Enables or disables the /mixton_listo and /mixton_no_listo Discord slash commands for the guild. The setting is persisted in the guild’s Firestore settings document. Request Body
enabled
boolean
required
true to enable the commands, false to disable them.
Response
{ "success": true, "commandEnabled": false }

POST /api/lobby/mixton-override-tier

Overrides the ELO tier bucket used to classify a player in the available players list. Setting tier to null removes the override and reverts to the player’s Faceit level. Request Body
discordId
string
required
Discord user ID of the player.
tier
string | null
required
One of "S", "A", "B", "C", or null to reset.
Response
{ "success": true, "tier": "A" }

POST /api/lobby/mixton-assign-player

Directly assigns a player to a team without waiting for the draft pick order. The player is removed from any other team they were previously on. A team can hold a maximum of 4 picked players (plus the captain). Request Body
teamId
string
required
Target team ID.
discordId
string
required
Discord user ID of the player to assign.
Response
{ "success": true, "state": { "...": "updated lobby state" } }

Draft Action

POST /api/lobby/mixton-pick

The captain whose turn it is picks a player from the available pool. Admins can also make picks on behalf of any captain. After a successful pick the currentPickIndex advances, the player is moved from availablePlayers to the team’s players array, and a WebSocket state broadcast is sent to all connected clients. Request Body
playerDiscordId
string
required
Discord user ID of the player to pick. The player must have mixton_ready: true in Firestore and not already be assigned to any team.
Response
{ "success": true, "state": { "...": "updated lobby state" } }
Returns 400 if the draft is not active, if the player is already picked, or if the player is not ready. Returns 403 if the caller is not the current captain and not an admin.

Match Launch

POST /api/lobby/create-match

Creates a manual 5v5 match from two arrays of Discord IDs. Validates all players against Firestore, creates two locked voice channels, moves members to their respective channels, posts a match announcement embed with result-reporting buttons, sends individual DMs to each player, and saves the match to faceit_active_matches. Request Body
teamA
array
required
Array of exactly 5 Discord user ID strings for Team A.
teamB
array
required
Array of exactly 5 Discord user ID strings for Team B.
Response
{
  "success": true,
  "matchId": "manual_1700000000000_ab1cd2"
}
Returns 400 if either team does not have exactly 5 players, or if any player is unregistered or belongs to a different guild (for admin role).

GET /api/lobby/guilds

Returns the list of guilds accessible to the authenticated user. admin users only see their own guild; global_admin users see all guilds the bot has joined. Returns 403 for non-admin users. Response
{
  "guilds": [
    { "id": "987654321098765432", "name": "Mixton Arena" }
  ]
}

WebSocket

WS /api/lobby/mixton-ws

A persistent WebSocket connection that streams real-time lobby state updates to all connected clients. The upgrade is handled at the HTTP server level via the ws library — it does not go through the standard Express router. Connection URL
wss://your-domain.com/api/lobby/mixton-ws?guildId=987654321098765432
guildId
string
Filter state broadcasts to a specific guild. If omitted, the connection receives updates for the primary DISCORD_GUILD_ID.
On Connect Immediately after the WebSocket handshake, the server sends a full state snapshot:
{
  "type": "state_update",
  "data": {
    "state": { "teams": [], "pickOrder": [], "currentPickIndex": 0, "draftStarted": false, "draftFinished": false },
    "availablePlayers": { "S": [], "A": [], "B": [], "C": [] },
    "commandEnabled": true,
    "guildName": "Mixton Arena",
    "guildId": "987654321098765432"
  }
}
On Lobby Action Every REST endpoint that modifies lobby state (setup, captain assignment, pick, reset, etc.) triggers a broadcast. All connected clients with a matching guildId receive a fresh state_update message with the full current state. Client Example
const ws = new WebSocket('wss://your-domain.com/api/lobby/mixton-ws?guildId=987654321098765432');

ws.onopen = () => {
  console.log('Connected to Mixton lobby stream');
};

ws.onmessage = (event) => {
  const { type, data } = JSON.parse(event.data);

  if (type === 'state_update') {
    console.log('Current teams:', data.state.teams);
    console.log('Available S-tier players:', data.availablePlayers.S);
    console.log('Draft active:', data.state.draftStarted);

    const currentTeamId = data.state.pickOrder[data.state.currentPickIndex];
    const currentTeam = data.state.teams.find(t => t.id === currentTeamId);
    if (currentTeam) {
      console.log("It's", currentTeam.captain?.faceit_nickname, "'s turn to pick");
    }
  }
};

ws.onclose = () => {
  console.log('Disconnected — consider reconnecting');
};
The WebSocket connection does not require a JWT cookie at the transport level. Authentication is enforced at the REST API level before any state-modifying action is processed. If you need to restrict WebSocket connections, you can validate the cookie during the HTTP upgrade handshake in the server configuration.

Build docs developers (and LLMs) love