Status Flow
Lobbies never dead-end. They either stay alive (rematch-ready) or get deleted entirely. There’s no “completed” terminal state.Self-healing lobbies. When the game server stops — whether the host clicks “End Match”, the server crashes, or its TTL expires — the lobby automatically returns to
waiting with all players retained. There is no completed status. Players can queue or start another match immediately.Creating a Lobby
Fresh lobby on reconnect
SetforceFresh: true on create to atomically replace a lobby the caller already hosts in this config. The backend deletes the old lobby (stopping its game server best-effort) and then creates the new one — all in a single call. It’s a no-op when the player has no prior lobby, so it’s safe to always pass on reconnect. This replaces the old 3-call workaround (TryReconnect → detect stale host lobby → DeleteLobby → CreateLobby) with one request.
409 Player is already in a lobby — they need to leave that one first.
Joining a Lobby
Two ways: by invite code or by lobby ID.- By Invite Code
- By Lobby ID
Join Errors
Updating Player State
Every player can update their own state. UsePATCH /me:
State merges, not replaces. Existing keys are preserved unless your update overwrites them. To clear a key, set it to
null.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 get403 Only the host can perform this action.
Update Lobby Settings
Kick a Player
x-player-id). The {playerId} in the path is the target.
Start the Game Directly
Skip matchmaking and launch a server immediately with the current players:server.status: "launching". Within ~10-30s status transitions to running and clients can connect. Read the connection host and port from the server response’s network_ports[] array — the external port is proxy-allocated and differs from your internal port.
playflow.json parity with matchmaking: a directly-started server receives
the same custom_data shape a matchmaker-launched one does — mode, teams
(grouped from each player’s state.team), players, lobby_id, match_id,
and version. A directly-created lobby never enters the queue, so declare the
mode yourself in lobby settings.mode (at create time, or later with
PATCH /me/settings) and your game server reads it from playflow.json as
custom_data.mode. If the lobby did queue at some point, the active
matchmaking mode wins.End the Match (Rematch)
waiting, and all players stay in the lobby with the same invite code and settings. Great for “Play Again” buttons.
If the game server stops on its own (crashes, hits TTL, or your game logic exits cleanly), the lobby auto-heals to
waiting with the same effect — no call needed. POST /me/end-match is just the explicit, host-triggered version.Leaving
Any player can leave at any time:Host leave behavior
What happens when the host leaves is controlled per lobby config viahostLeaveBehavior:
When to use each:
"promote"— persistent rooms, squad play, party-based games where the lobby should survive individual churn. This is the default for every lobby config."delete"— one-shot private rooms, pickup games, host-authored sessions where the host’s presence IS the room. Pairs well withisPrivate: trueinvite-only lobbies.
"delete".
Example config JSON:
When
hostLeaveBehavior: "delete" fires, any game server associated with the lobby is stopped best-effort — same cleanup path as the admin delete endpoint.Browsing Lobbies
Anyone (even without a player-id) can browse public lobbies in a config:isPrivate: true) are excluded automatically.
Query params:
Cleanup & Timeouts
Lobbies don’t stick around forever. The PlayFlowlobby-sweep cron runs continuously and handles:
- Expired waiting lobbies — deleted after
timeoutseconds (default 5 min) with no activity - Stale in-game lobbies — in-game lobbies past 2 hours are cleaned up
- Heartbeat expiry — if
heartbeat: trueis enabled in config, players that stop sending heartbeats are removed - Matchmaking timeout — lobbies in
in_queuepast the mode’s timeout drop back towaiting
Heartbeat
Setheartbeat: true on a lobby config to require connected players to ping the server periodically. Players who go silent get evicted by the lobby-sweep cron.
heartbeat— whentrue, every player must send heartbeatsheartbeatTimeout— seconds before a player is considered stale. Any player whose last heartbeat is older than this is removed on the next sweep.
/me/events isn’t available. Mobile background states, low-resource IoT clients, or environments that block long-lived connections.
Admin Delete
Force-delete any lobby by ID. Removes all players and cancels any active matchmaking — regardless of who created the lobby.- No
x-player-idrequired. This is a server-side admin operation. {config}— the lobby config name (e.g."ranked_2v2").{id}— the lobby UUID to delete.
Invite Codes
inviteCodeConfig on a lobby config controls the codes PlayFlow generates when a lobby is created. Every new lobby (public and private) gets a code so friends can join by sharing it.
With the example above a lobby might get the code
PLAY-K7Q2F1. Players then join with:
Error Codes
Every lobby error returns the same JSON shape — a human-readableerror message, a detail (the same message), and the HTTP status: