Skip to main content
A lobby progresses through several states during its lifetime. Understanding the lifecycle helps you design your game’s UI and handle every edge case cleanly.

Status Flow

waiting ─────→ in_queue ─────→ matched ─────→ in_game ─────→ waiting (round end)
    │             │               │               │
    │             │               │               └──→ waiting (server stopped)
    │             │               └──→ in_queue (server create failed)
    │             └──→ waiting (timeout / cancel / player leave)

    └──→ starting ─────→ in_game (host started game directly)

             └──→ waiting (server create failed)
StatusMeaning
waitingOpen lobby, accepting players
in_queueMatchmaking active, searching for opponents
startingHost triggered direct start, server provisioning
matchedClaimed for a match, server being created
match_foundMatch proposed, waiting for player confirmations (optional)
in_gameGame server running, players connected
finishedGame complete (rarely used — most lobbies return to waiting)

Creating a Lobby

POST /v3/lobbies/{config}
x-player-id: host_player
Content-Type: application/json

{
  "name": "Friday Night CTF",
  "maxPlayers": 8,
  "region": "us-east",
  "isPrivate": false,
  "allowLateJoin": true,
  "settings": { "map": "forest", "mode": "ctf" },
  "state": { "ready": true }
}
The creator becomes the host. An invite code is generated automatically (even for public lobbies, so friends can share it).
FieldRequiredDefaultNotes
nameYes-Display name
maxPlayersNo21–100
regionNous-eastGame server region
isPrivateNofalseHide from GET /v3/lobbies/{config} browse
allowLateJoinNotrueCan players join after game starts?
settingsNo{}Game-specific data passed to the server
stateNonullHost’s initial player state
The host can only be in one lobby at a time per config. Trying to create a second returns 409 Already in another lobby.

Joining a Lobby

Two ways: by invite code or by lobby ID.
POST /v3/lobbies/{config}/join
x-player-id: new_player

{
  "code": "MEOW-42",
  "state": { "team": "blue" }
}
Best for “friend shares a code” flow. Works for both public and private lobbies.

Join Errors

StatusErrorCause
404Lobby not foundWrong ID/code
400Lobby is fullcurrentPlayers >= maxPlayers
400Lobby is not accepting playersstatus: in_game and allowLateJoin: false
409Already in another lobbyPlayer is in a different lobby in the same config
Joining is idempotent — if the player is already in the lobby, it returns the lobby unchanged (no error).

Updating Player State

Every player can update their own state. Use PATCH /me:
PATCH /v3/lobbies/{config}/me
x-player-id: my_player

{
  "state": {
    "ready": true,
    "character": "wizard",
    "mmr": 1450
  }
}
State merges, not replaces. Existing keys are preserved unless your update overwrites them. To clear a key, set it to null.
State flows automatically into matchmaking_data when the lobby queues, so fields like mmr are used by matchmaking rules.

Host Actions

Only the host can perform these actions. Non-hosts get 403 Not host.

Update Lobby Settings

PATCH /v3/lobbies/{config}/me/settings
x-player-id: host_player

{
  "name": "New Name",
  "maxPlayers": 16,
  "isPrivate": true,
  "region": "eu-west",
  "settings": { "map": "desert" }
}
Every field is optional — only include what you want to change.

Kick a Player

DELETE /v3/lobbies/{config}/me/players/{playerId}
x-player-id: host_player
The kicker is the host (from x-player-id). The {playerId} in the path is the target.
Hosts can’t kick themselves. Use DELETE /me to leave (which auto-transfers host).

Start the Game Directly

Skip matchmaking and launch a server immediately with the current players:
POST /v3/lobbies/{config}/me/start
x-player-id: host_player
The response has server.status: "launching". Within ~10-30s (or ~5s with pool servers), status transitions to running and clients can connect.

End the Round

DELETE /v3/lobbies/{config}/me/game
x-player-id: host_player
The server stops, lobby returns to waiting, players stay in the lobby.
For matchmaking games, ending just clears this lobby’s reference to the shared server — the actual server stops when all matched lobbies end. For direct games (started via /me/start), ending stops the server entirely.

Leaving

Any player can leave at any time:
DELETE /v3/lobbies/{config}/me
x-player-id: my_player
What happens depends on who leaves:
ScenarioBehavior
Regular player leavesRemoved from players, currentPlayers decrements
Host leaves (with others remaining)Host auto-transfers to next player
Last player leavesLobby is deleted, returns {"status": "lobby_deleted"}
Player leaves during matchmakingMatchmaking auto-cancels, lobby returns to waiting

Transferring Host Manually

The host can pass leadership to another player:
POST /v3/lobbies/{config}/me/host/{playerId}
x-player-id: current_host
Useful when the host needs to AFK or hand off the lobby.

Browsing Lobbies

Anyone (even without a player-id) can browse public lobbies in a config:
GET /v3/lobbies/{config}?region=us-east&status=waiting&limit=20
Returns a paginated list. Private lobbies (isPrivate: true) are excluded automatically. Query params:
ParamPurpose
regionFilter by region
statusFilter by lobby status
limitPage size (default 50, max 100)
offsetPagination offset

Cleanup & Timeouts

Lobbies don’t stick around forever. The PlayFlow lobby-sweep cron runs continuously and handles:
  • Expired waiting lobbies — deleted after timeout seconds (default 5 min) with no activity
  • Stale in-game lobbies — in-game lobbies past 2 hours are cleaned up
  • Heartbeat expiry — if heartbeat: true is enabled in config, players that stop sending heartbeats are removed
  • Matchmaking timeout — lobbies in in_queue past the mode’s timeout drop back to waiting
For most games, you don’t need to send heartbeats. The SSE connection itself is the heartbeat — as long as the player is connected to /me/events, they’re alive. Only enable heartbeat in config if your players use HTTP polling instead of SSE.

Error Codes

HTTPCodeMeaning
400LOBBY_FULLCapacity exceeded
400LOBBY_NOT_ACCEPTINGIn-game with no late-join
400INVALID_ACTIONE.g., host trying to self-kick
403NOT_HOSTNon-host tried a host-only action
404LOBBY_NOT_FOUNDLobby doesn’t exist
404PLAYER_NOT_FOUNDPlayer isn’t in the lobby
409ALREADY_IN_LOBBYPlayer is already in another lobby
409GAME_ALREADY_RUNNINGStarted a game that’s already in progress
409INVALID_STATEWrong status for this operation
All errors follow the same format:
{
  "error": "Lobby is full",
  "detail": "Lobby is full (4 of 4)",
  "status": 400
}