> ## Documentation Index
> Fetch the complete documentation index at: https://docs.playflowcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Matchmaking Guide

> Integrate traditional and role-based matchmaking with the lobby system.

PlayFlow's matchmaking system supports both **traditional symmetric matchmaking** (like Fortnite, Counter-Strike) and **advanced role-based asymmetric matchmaking** (like League of Legends, Overwatch, Dead by Daylight). The system seamlessly integrates with lobbies to provide automated matching based on skill, region, roles, and custom criteria.

## Prerequisites

Matchmaking always runs on top of the lobby and player-identity systems, so before starting a search you need:

* **A lobby.** Every match request comes from a lobby (even a solo one). See the [Lobby Quick Start](/unity/lobbies) to create and manage lobbies.
* **A player identity.** Lobby and matchmaking calls require an authenticated player. With a client key and an auth provider configured, pass a valid player token; otherwise a trusted player id is used. See [Player Authentication](/guides/player-authentication).
* **A matchmaking mode.** Configure at least one mode in your lobby configuration (below) whose name you pass to `FindMatch`.

## Matchmaking Types

### Traditional/Symmetric Matchmaking

Used for games where all players have equal capabilities and teams are symmetrical. Examples include:

* **Battle Royale** (Fortnite, PUBG): 100 players, last one standing
* **Team Deathmatch** (Counter-Strike, Valorant): 5v5 competitive matches
* **Arena Duels**: 1v1 or 2v2 ranked matches
* **Large-scale Battles**: 32v32 or 64v64 team warfare

### Role-Based/Asymmetric Matchmaking

Used for games where players have distinct roles with unique abilities and responsibilities. Examples include:

* **MOBA Games** (League of Legends, Dota 2): Support, Tank, DPS roles with team composition requirements
* **Hero Shooters** (Overwatch, Paladins): Role queue with tanks, healers, and damage dealers
* **Asymmetric Horror** (Dead by Daylight): 4 survivors vs 1 killer
* **Social Deduction** (Among Us, Werewolf): Different roles with hidden information

## How Matchmaking Works

In PlayFlow, matchmaking always works through lobbies:

1. **A player creates a lobby** (for just themself, or for a party of friends)
2. Players set their matchmaking data (skill, region, roles)
3. The **lobby host starts the search**
4. The system finds compatible lobbies based on your configuration
5. When a match is found, a game server automatically starts for all players

This lobby-based approach naturally supports party matchmaking (friends queuing together) while maintaining team cohesion.

## Dashboard Configuration

Before writing code, you must configure your matchmaking rules in the PlayFlow Dashboard.

1. Go to your project's **Configuration** → **Lobbies** tab
2. Create or edit a lobby configuration
3. Click the **Matchmaking** tab
4. Add and configure matchmaking modes

### Traditional Configuration Examples

<Tabs>
  <Tab title="5v5 Competitive">
    For tactical shooters like Counter-Strike or Valorant:

    ```json theme={null}
    {
      "Competitive5v5": {
        "teams": 2,
        "playersPerTeam": 5,
        "timeout": 120,
        "backfill": { "enabled": false },
        "rules": [
          {
            "type": "difference",
            "field": "mmr",
            "maxDifference": 200
          },
          {
            "type": "region"
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Battle Royale">
    For games like Fortnite or PUBG:

    ```json theme={null}
    {
      "BattleRoyaleSolo": {
        "teams": 100,
        "playersPerTeam": 1,
        "minPlayersPerTeam": 1,
        "timeout": 60,
        "backfill": { "enabled": true },
        "rules": [
          {
            "type": "difference",
            "field": "mmr",
            "maxDifference": 500
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Team Deathmatch">
    For casual multiplayer modes:

    ```json theme={null}
    {
      "TeamDeathmatch": {
        "teams": 2,
        "playersPerTeam": 8,
        "minPlayersPerTeam": 4,
        "timeout": 45,
        "backfill": { "enabled": true },
        "maxPartySize": 4,
        "rules": [
          {
            "type": "difference",
            "field": "mmr",
            "maxDifference": 300
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="1v1 Ranked">
    For fighting games or RTS duels:

    ```json theme={null}
    {
      "RankedDuel": {
        "teams": 2,
        "playersPerTeam": 1,
        "timeout": 180,
        "backfill": { "enabled": false },
        "rules": [
          {
            "type": "difference",
            "field": "mmr",
            "maxDifference": 100
          },
          {
            "type": "equals",
            "field": "gameMode"
          }
        ]
      }
    }
    ```
  </Tab>
</Tabs>

### Role-Based Configuration Examples

<Tabs>
  <Tab title="MOBA (LoL/Dota)">
    For games like League of Legends with strict role requirements:

    ```json theme={null}
    {
      "RankedRoleQueue": {
        "teams": 2,
        "playersPerTeam": 5,
        "minPlayersPerTeam": 5,
        "maxPartySize": 2,
        "excludedFromQueue": ["Spectator", "Coach"],
        "teamComposition": {
          "team1": {
            "top": 1,
            "jungle": 1,
            "mid": 1,
            "adc": 1,
            "support": 1
          },
          "team2": {
            "top": 1,
            "jungle": 1,
            "mid": 1,
            "adc": 1,
            "support": 1
          }
        },
        "timeout": 90,
        "rules": [
          {
            "type": "difference",
            "field": "mmr",
            "maxDifference": 150
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Hero Shooter (Overwatch)">
    For role-locked competitive modes:

    ```json theme={null}
    {
      "CompetitiveRoleQueue": {
        "teams": 2,
        "playersPerTeam": 6,
        "minPlayersPerTeam": 6,
        "maxPartySize": 3,
        "excludedFromQueue": ["Spectator"],
        "teamComposition": {
          "team1": {
            "tank": 2,
            "damage": 2,
            "support": 2
          },
          "team2": {
            "tank": 2,
            "damage": 2,
            "support": 2
          }
        },
        "timeout": 120,
        "backfill": { "enabled": true },
        "rules": [
          {
            "type": "difference",
            "field": "sr",
            "maxDifference": 250
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Asymmetric Horror">
    For games like Dead by Daylight:

    ```json theme={null}
    {
      "AsymmetricHorror": {
        "teams": 2,
        "playersPerTeam": 4,
        "minPlayersPerTeam": 1,
        "maxPartySize": 4,
        "excludedFromQueue": ["Spectator"],
        "teamComposition": {
          "survivors": {
            "survivor": 4
          },
          "killer": {
            "killer": 1
          }
        },
        "timeout": 60,
        "backfill": { "enabled": false },
        "rules": [
          {
            "type": "difference",
            "field": "rank",
            "maxDifference": 6
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Social Deduction">
    For games like Among Us or Town of Salem:

    ```json theme={null}
    {
      "SocialDeduction": {
        "teams": 2,
        "playersPerTeam": 8,
        "minPlayersPerTeam": 6,
        "maxPartySize": 3,
        "excludedFromQueue": ["Spectator", "Host"],
        "teamComposition": {
          "crew": {
            "crewmate": 6,
            "detective": 1,
            "engineer": 1
          },
          "impostors": {
            "impostor": 2
          }
        },
        "timeout": 45,
        "backfill": { "enabled": true },
        "rules": [
          {
            "type": "difference",
            "field": "trustScore",
            "maxDifference": 200
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="MMO Dungeon">
    For dungeon finder systems:

    ```json theme={null}
    {
      "DungeonFinder": {
        "teams": 1,
        "playersPerTeam": 5,
        "minPlayersPerTeam": 5,
        "maxPartySize": 5,
        "excludedFromQueue": ["Offline"],
        "teamComposition": {
          "party": {
            "tank": 1,
            "healer": 1,
            "dps": 3
          }
        },
        "timeout": 300,
        "backfill": { "enabled": true },
        "rules": [
          {
            "type": "difference",
            "field": "itemLevel",
            "maxDifference": 40
          }
        ]
      }
    }
    ```
  </Tab>
</Tabs>

### Backfill Configuration

Backfill routes newly-queued players into an already-running match that still has open slots, instead of always spinning up a fresh server. Configure it with the `backfill` object on a mode:

```json theme={null}
{
  "TeamDeathmatch": {
    "teams": 2,
    "playersPerTeam": 8,
    "minPlayersPerTeam": 4,
    "timeout": 45,
    "backfill": {
      "enabled": true,
      "windowSeconds": 300,
      "closeWhenFull": true,
      "skill": { "field": "mmr", "maxDifference": 300 }
    }
  }
}
```

| Field           | Default | Purpose                                                                                                     |
| --------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| `enabled`       | `false` | Master switch. Omit or set `false` to disable backfill.                                                     |
| `windowSeconds` | `300`   | How long after a match forms it stays open to joiners.                                                      |
| `closeWhenFull` | `true`  | Stop accepting backfill once the match reaches capacity.                                                    |
| `skill`         | none    | Optional skill gate — a joining lobby's average `field` must be within `maxDifference` of the match's band. |

<Info>
  `allowBackfill: true` is still accepted as a deprecated alias for `backfill: { "enabled": true }`, but prefer the object form so you can tune the window and skill gate.
</Info>

## Implementation Guide

Here are the steps to implement matchmaking in your game.

### Step 1: Set Player Matchmaking Data

Players must set their matchmaking attributes before searching. This includes MMR, regions, and roles (for role-based modes).

<Tabs>
  <Tab title="Unity">
    #### Traditional Matchmaking Data

    For traditional modes, set MMR and acceptable regions:

    ```csharp theme={null}
    using System.Collections.Generic;
    using PlayFlow;
    using UnityEngine;

    public class TraditionalMatchmaking : MonoBehaviour
    {
        public void SetTraditionalMatchmakingData(int playerMMR)
        {
            var matchmakingData = new Dictionary<string, object>
            {
                { "mmr", playerMMR },
                { "regions", new List<string> { "us-west", "us-east", "eu-west" } }
            };

            PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(matchmakingData,
                onSuccess: (lobby) => {
                    Debug.Log("Player matchmaking data has been set.");
                },
                onError: (error) => {
                    Debug.LogError($"Could not set matchmaking data: {error}");
                }
            );
        }
    }
    ```

    #### Role-Based Matchmaking Data

    For role-based modes, also specify your role preferences:

    ```csharp theme={null}
    public class RoleBasedMatchmaking : MonoBehaviour
    {
        // Single specific role (MOBA player who only plays one lane)
        public void SetSpecificRole(string role, int playerMMR)
        {
            var matchmakingData = new Dictionary<string, object>
            {
                { "role", role }, // e.g., "jungle", "mid", "support"
                { "mmr", playerMMR },
                { "regions", new List<string> { "us-east", "us-west" } }
            };

            PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(matchmakingData);
        }

        // Priority array of roles (flexible fill player)
        public void SetFlexibleRoles(int playerMMR)
        {
            var matchmakingData = new Dictionary<string, object>
            {
                { "role", new List<string> { "support", "adc", "mid" } }, // Preference order
                { "mmr", playerMMR },
                { "regions", new List<string> { "us-east", "eu-west" } }
            };

            PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(matchmakingData);
        }

        // Spectator role (excluded from matchmaking)
        public void SetSpectatorRole()
        {
            var matchmakingData = new Dictionary<string, object>
            {
                { "role", "Spectator" }, // Will be excluded from team requirements
                { "regions", new List<string> { "us-east" } }
            };

            PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(matchmakingData);
        }
    }
    ```
  </Tab>

  <Tab title="Unreal Engine">
    Coming soon...
  </Tab>

  <Tab title="Godot">
    Coming soon...
  </Tab>
</Tabs>

### Step 2: Start the Search

Once the player's data is set, the lobby host can start the search by calling `FindMatch`. The `mode` parameter must match the name of the matchmaking mode you created in the dashboard.

<Tabs>
  <Tab title="Unity">
    ```csharp theme={null}
    public class MatchmakingStarter : MonoBehaviour
    {
        // Traditional matchmaking (Competitive5v5, Duels, BattleRoyale, etc.)
        public void StartTraditionalSearch(string mode = "Competitive5v5")
        {
            var manager = PlayFlowLobbyManagerV2.Instance;

            if (!manager.IsInLobby || !manager.IsHost)
            {
                Debug.LogWarning("Must be the host of a lobby to start matchmaking.");
                return;
            }

            manager.FindMatch(mode,
                onSuccess: (lobby) => {
                    Debug.Log($"Successfully entered the '{lobby.matchmaking.mode}' queue.");
                    // Update your UI to show a "searching..." state
                },
                onError: (error) => {
                    Debug.LogError($"Failed to start matchmaking: {error}");
                }
            );
        }

        // Role-based matchmaking (RankedRoleQueue, AsymmetricHorror, etc.)
        public void StartRoleBasedSearch(string mode = "RankedRoleQueue")
        {
            var manager = PlayFlowLobbyManagerV2.Instance;

            if (!manager.IsInLobby || !manager.IsHost)
            {
                Debug.LogWarning("Must be the host of a lobby to start matchmaking.");
                return;
            }

            // Ensure all players have set their roles
            var currentLobby = manager.CurrentLobby;
            bool allPlayersHaveRoles = true;

            foreach (var playerId in currentLobby.players)
            {
                if (currentLobby.lobbyStateRealTime.TryGetValue(playerId, out var playerState))
                {
                    if (!playerState.ContainsKey("role"))
                    {
                        Debug.LogWarning($"Player {playerId} has not set their role!");
                        allPlayersHaveRoles = false;
                    }
                }
            }

            if (!allPlayersHaveRoles)
            {
                Debug.LogError("All players must set their role before matchmaking!");
                return;
            }

            manager.FindMatch(mode,
                onSuccess: (lobby) => {
                    Debug.Log($"Entered role-based queue: {lobby.matchmaking.mode}");
                    // Show searching UI with role information
                },
                onError: (error) => {
                    Debug.LogError($"Failed to start role-based matchmaking: {error}");
                }
            );
        }
    }
    ```
  </Tab>

  <Tab title="Unreal Engine">
    Coming soon...
  </Tab>

  <Tab title="Godot">
    Coming soon...
  </Tab>
</Tabs>

### Step 3: Handle Matchmaking Events

After starting the search, subscribe to events to drive your UI.

| Event                         | When it fires                                                |
| ----------------------------- | ------------------------------------------------------------ |
| `OnMatchmakingStarted`        | Lobby entered the queue (status → `in_queue`)                |
| `OnQueueStats`                | Every \~10s while queueing — live wait-time telemetry        |
| `OnMatchAwaitingConfirmation` | Match proposal pending (only if `matchConfirmation.enabled`) |
| `OnMatchConfirmed`            | This lobby accepted; waiting on others                       |
| `OnMatchDeclined`             | Someone declined or timed out — back to `waiting`            |
| `OnMatchFound`                | Match committed, server launching                            |
| `OnMatchRunning`              | Server is ready to accept connections                        |
| `OnMatchServerDetailsReady`   | Full port list (for multi-port games)                        |
| `OnMatchmakingCancelled`      | Host called `CancelMatchmaking()`                            |
| `OnMatchmakingTimeout`        | Queue timed out without finding a match                      |

<Tabs>
  <Tab title="Unity">
    ```csharp theme={null}
    void Start()
    {
        var events = PlayFlowLobbyManagerV2.Instance.Events;

        events.OnMatchmakingStarted.AddListener(HandleMatchmakingStarted);
        events.OnQueueStats.AddListener(HandleQueueStats);
        events.OnMatchFound.AddListener(HandleMatchFound);
        events.OnMatchRunning.AddListener(HandleMatchRunning);
        events.OnMatchServerDetailsReady.AddListener(HandleServerDetailsReady);
        events.OnMatchmakingCancelled.AddListener(HandleMatchmakingCancelled);
        events.OnMatchmakingTimeout.AddListener(HandleMatchmakingTimeout);
    }

    private void HandleMatchmakingStarted(Lobby lobby)
    {
        Debug.Log($"In queue for mode: {lobby.matchmaking.mode}");
        // Show "Searching for match…" UI
    }

    private void HandleQueueStats(QueueStats stats)
    {
        // Update your UI: "42 players searching · ~12s avg wait"
        Debug.Log($"{stats.playersSearching} searching, {stats.avgWaitSeconds:F0}s avg wait");
    }

    private void HandleMatchFound(Lobby lobby)
    {
        Debug.Log("Match committed! Server launching…");
        // Update UI to "Connecting…"
    }

    private void HandleMatchRunning(ConnectionInfo connectionInfo)
    {
        // Quick-access for single-port games
        MyNetworkManager.Connect(connectionInfo.Ip, connectionInfo.Port);
    }

    private void HandleServerDetailsReady(List<PortMappingInfo> portMappings)
    {
        // Preferred: look up by the port name you defined in the dashboard
        var lobby = PlayFlowLobbyManagerV2.Instance.CurrentLobby;
        if (lobby.TryGetPort("game_udp", out var port))
        {
            Debug.Log($"Connecting to {port.host}:{port.external_port}");
            MyNetworkManager.Connect(port.host, port.external_port);
        }
    }

    private void HandleMatchmakingCancelled(Lobby lobby) { /* back to lobby */ }
    private void HandleMatchmakingTimeout(Lobby lobby)   { /* show "no match found" */ }
    ```
  </Tab>

  <Tab title="Unreal Engine">
    Coming soon...
  </Tab>

  <Tab title="Godot">
    Coming soon...
  </Tab>
</Tabs>

### Step 3.5: CS2-Style "Accept Match" flow (optional)

If the matchmaking mode has `matchConfirmation.enabled: true` in the dashboard, matches pause at the `match_found` status waiting for every lobby to explicitly accept. Opt in by subscribing to three extra events and exposing accept/decline buttons.

```csharp theme={null}
void Start()
{
    var events = PlayFlowLobbyManagerV2.Instance.Events;
    events.OnMatchAwaitingConfirmation.AddListener(HandleAwaitingConfirmation);
    events.OnMatchConfirmed.AddListener(_  => Debug.Log("You accepted. Waiting on other players…"));
    events.OnMatchDeclined.AddListener(_   => Debug.Log("Match cancelled — re-queue when ready."));
}

void HandleAwaitingConfirmation(Lobby lobby)
{
    // Show your "Accept Match" dialog
    var deadlineIso = lobby.matchmaking.confirmation.deadline;
    Debug.Log($"Match found — accept before {deadlineIso}");
}

public void OnAcceptClicked()  => PlayFlowLobbyManagerV2.Instance.ConfirmMatch();
public void OnDeclineClicked() => PlayFlowLobbyManagerV2.Instance.DeclineMatch();
```

If **any** player declines or the deadline expires, every participating lobby returns to `waiting` (not back to `in_queue`) — players re-queue manually when ready. This matches how CS2 and League of Legends handle match acceptance.

### Step 4: Cancel the Search

The host can cancel the matchmaking search at any time by calling `CancelMatchmaking`.

<Tabs>
  <Tab title="Unity">
    ```csharp theme={null}
    public void CancelSearch()
    {
        var manager = PlayFlowLobbyManagerV2.Instance;

        if (!manager.IsHost || manager.CurrentLobby?.status != "in_queue")
        {
            return; // Can only cancel if you are the host and in the queue
        }

        manager.CancelMatchmaking(
            onSuccess: (lobby) => {
                Debug.Log("Successfully cancelled matchmaking.");
            },
            onError: (error) => {
                Debug.LogError($"Failed to cancel matchmaking: {error}");
            }
        );
    }
    ```
  </Tab>

  <Tab title="Unreal Engine">
    Coming soon...
  </Tab>

  <Tab title="Godot">
    Coming soon...
  </Tab>
</Tabs>

## Advanced Features

### Role Assignment Algorithm

The role-based matchmaking system uses an intelligent priority-based algorithm to assign players to roles. Roles can be provided as either a single string or a string array for flexibility.

<Info>
  **Key Principle**: Players with fewer role options get priority assignment to ensure everyone gets a suitable role. This is NOT random - it's deterministic and fair.
</Info>

#### How Priority Assignment Works

**1. Specificity First**
Players with fewer role options are assigned before flexible players:

```csharp theme={null}
// Assignment priority (highest to lowest):
Player A: ["tank"]                    // 1 option - HIGHEST PRIORITY
Player B: ["tank", "healer"]          // 2 options - medium priority
Player C: ["tank", "healer", "dps"]   // 3 options - lowest priority

// Player A gets first choice, then B, then C
```

**2. Preference Order Matters**
When assigned, the system tries roles left-to-right in your array:

```csharp theme={null}
// Player's preference array:
["support", "adc", "mid"]

// System tries:
// 1st: Can they be support? → If yes, assigned!
// 2nd: If not, can they be adc? → If yes, assigned!
// 3rd: If not, can they be mid? → If yes, assigned!
```

**3. Tie-Breaking**
When players have identical preferences, first-come-first-served applies:

```csharp theme={null}
// Both want the same roles:
Player1: ["support", "adc"]  // Joined lobby first
Player2: ["support", "adc"]  // Joined lobby second

// Result:
Player1 → Gets "support" (first preference)
Player2 → Gets "adc" (support taken, gets second choice)
```

#### Real-World Example

For a MOBA team needing specific roles:

```csharp theme={null}
// Team needs: 1 top, 1 jungle, 1 mid, 1 adc, 1 support

// Players with their preferences:
Player1: ["mid", "top", "support"]     // Flexible
Player2: ["jungle"]                    // One-trick (gets priority!)
Player3: ["support", "adc"]            // Support main
Player4: ["mid"]                       // Mid only
Player5: ["top", "mid", "jungle"]      // Fill player

// Assignment result:
Player2 → jungle (most specific, only wants jungle)
Player4 → mid (most specific for mid)
Player3 → support (first preference available)
Player5 → top (mid/jungle taken, gets top)
Player1 → adc (forced to fill, but match succeeds!)
```

#### Setting Role Preferences in Code

```csharp theme={null}
public class RolePreferenceManager : MonoBehaviour
{
    // Specific player - highest priority but risky
    public void SetOneRoleOnly(string role)
    {
        var data = new Dictionary<string, object> {
            { "role", role }  // Single string
        };
        PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(data);
    }

    // Flexible player - lower priority but guarantees match
    public void SetMultipleRoles(List<string> roles)
    {
        var data = new Dictionary<string, object> {
            { "role", roles }  // String array in preference order
        };
        PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(data);
    }

    // Ultra-flexible - can play anything
    public void SetFillPlayer()
    {
        var allRoles = new List<string> {
            "top", "jungle", "mid", "adc", "support"
        };
        var data = new Dictionary<string, object> {
            { "role", allRoles }
        };
        PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(data);
    }
}
```

<Warning>
  **Edge Case**: If too many players are inflexible (single role only) and want the same role, the match will fail. Always encourage some flexibility in your player base!
</Warning>

### Spectator Support

Spectators are excluded from team requirements but still receive game server access for observation.

```csharp theme={null}
public class SpectatorManager : MonoBehaviour
{
    public void JoinAsSpectator()
    {
        // Set spectator role
        var spectatorData = new Dictionary<string, object>
        {
            { "role", "Spectator" },
            { "regions", new List<string> { "us-east" } }
        };

        PlayFlowLobbyManagerV2.Instance.UpdatePlayerState(spectatorData);
    }

    private void OnMatchFound(Lobby lobby)
    {
        // Spectators still get notified and can connect
        if (IsSpectator())
        {
            Debug.Log("Joined match as spectator!");
            // Connect to server in spectator mode
        }
    }
}
```

### Regional Matchmaking

Players can specify multiple acceptable regions. The system finds matches where players have overlapping regions.

```csharp theme={null}
// Specify regions in order of preference
var regionList = new List<string> { "us-east", "us-west", "eu-west" };
UpdatePlayerState(new Dictionary<string, object> {
    { "regions", regionList }
});
```

### Party Support

The lobby-based approach naturally supports parties. Friends can join the same lobby and queue together:

```csharp theme={null}
public class PartyManager : MonoBehaviour
{
    public void CreatePartyLobby()
    {
        // Create lobby for your party
        PlayFlowLobbyManagerV2.Instance.CreateLobby(
            name: "My Party",
            maxPlayers: 4,  // Your party size
            isPrivate: true,
            onSuccess: (lobby) => {
                Debug.Log($"Party lobby created! Share code: {lobby.code}");
            }
        );
    }

    public void QueueWithParty(string mode)
    {
        // Everyone in the lobby queues together
        PlayFlowLobbyManagerV2.Instance.FindMatch(mode);
    }
}
```

## Troubleshooting

### Common Issues

<AccordionGroup>
  <Accordion title="Players not matching despite being in queue">
    * Verify all players have set required matchmaking data (MMR, regions, roles)
    * Check that players have overlapping regions in their `regions` field
    * For role-based modes, ensure you have the exact roles needed for team composition
    * Check dashboard configuration timeout settings
  </Accordion>

  <Accordion title="Spectators being counted in matchmaking">
    Ensure spectator roles are listed in the `excludedFromQueue` array in your dashboard configuration:

    ```json theme={null}
    "excludedFromQueue": ["Spectator", "Observer"]
    ```
  </Accordion>

  <Accordion title="Role assignment not working correctly">
    * Players with single specific roles are prioritized over flexible players
    * Ensure role names match exactly with dashboard configuration
    * Check that total players match team requirements
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={3}>
  <Card title="Lobby Quick Start" icon="rocket" href="/unity/lobbies">
    Review the basics of creating and managing lobbies.
  </Card>

  <Card title="Lobby Events Reference" icon="radio" href="/unity/lobby-events">
    See all events for building responsive matchmaking UI.
  </Card>

  <Card title="Game Servers" icon="server" href="/unity/game-servers">
    Learn about server lifecycle and configuration.
  </Card>
</CardGroup>
