The Endpoint
- One connection per player is enough — all events for their current lobby flow through it
- The connection stays open indefinitely (with keep-alive pings every 30s)
- When the player leaves the lobby, close the connection
Player auth: If your project has a player-auth provider configured (
custom_jwt, playfab, or steam), client-key (pfclient_*) requests must also send an x-player-token: {verified token} header — x-player-id alone only works when the provider is none or when you use a server key (pf_*). Without it, every request below returns 401. See Player Authentication for setup.The SSE connection doubles as a heartbeat. As long as the stream is open, PlayFlow knows the player is alive. You don’t need a separate heartbeat endpoint unless you’re not using SSE (e.g., polling clients on legacy platforms).
Event Types
Event: connected
Fires immediately on connection. Gives you the full current state — no need to call GET /me first.
Event: lobby_updated
Fires whenever the lobby changes:
- A player joins or leaves
- Host kicks someone
- A player updates their state
- Host changes settings
- Host starts matchmaking / game
- Match found → status becomes
in_game - Game server status changes (launching → running)
status becomes in_game and server.status becomes running, read host and external_port from server.network_ports[] (never assume a fixed port — the external port is proxy-allocated) and connect your game client.
Event: queue_stats
Only fires while status: in_queue. Emitted every 10 seconds.
Event: lobby_deleted
Fires when the lobby is deleted (last player left, host deleted it, or cleanup timeout).
reason field tells you why the lobby closed, so you can differentiate a completed match from a timeout:
Close the connection on this event — there’s nothing left to subscribe to.
Event: ping
Keep-alive every 30 seconds. Carries a { ts } payload (epoch milliseconds). You can ignore these in your handler — they exist only to keep the connection alive through proxies and load balancers.
Client Examples
- JavaScript (Browser)
- Unity (C#)
- Godot (GDScript)
- curl (testing)
Handling Disconnects
SSE connections can drop due to network issues, proxy timeouts, or server restarts. Your client should:- Detect disconnect — the read loop exits or the connection errors
- Reconnect automatically — with exponential backoff (1s, 2s, 4s, max 30s)
- Replay state on reconnect — the
connectedevent sends the current full state, so you can recover cleanly
Polling as a Fallback
If SSE isn’t available (e.g., your platform blocks streaming), pollGET /me instead:
heartbeat: true in your lobby config and send a heartbeat on each tick (well under heartbeatTimeout seconds). Without SSE keeping the player implicitly alive, the lobby-sweep cron will otherwise evict inactive players:
The Full Matchmaking Flow with SSE
This is what a player’s event stream looks like during a matchmaking session:Matchmaking
How matchmaking finds opponents and forms matches.
API Reference
Full SSE endpoint reference.