Skip to main content
PlayFlow provides a single SSE endpoint per player that streams everything you need: lobby updates, queue stats, match notifications, and server status changes. No polling required.

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)
Key usage: When 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.
Use this to show live “42 searching, ~12s wait” in your matchmaking UI.

Event: lobby_deleted

Fires when the lobby is deleted (last player left, host deleted it, or cleanup timeout).
The 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


Handling Disconnects

SSE connections can drop due to network issues, proxy timeouts, or server restarts. Your client should:
  1. Detect disconnect — the read loop exits or the connection errors
  2. Reconnect automatically — with exponential backoff (1s, 2s, 4s, max 30s)
  3. Replay state on reconnect — the connected event sends the current full state, so you can recover cleanly
While disconnected, you won’t receive events. You might miss a match, a kick, or a server ready event. Your reconnect logic should fetch GET /me on reconnect to ensure state consistency, OR rely on the connected event’s initial state.

Polling as a Fallback

If SSE isn’t available (e.g., your platform blocks streaming), poll GET /me instead:
When polling, enable 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:
That’s the entire competitive matchmaking experience — queue, find opponents, launch server, connect — delivered through one SSE stream.

Matchmaking

How matchmaking finds opponents and forms matches.

API Reference

Full SSE endpoint reference.