Realtime Updates
Phoenix offers a managed WebSocket channel for live leaderboard streams. Use it when you need immediate score changes in your game client, spectator dashboard, or broadcast overlays.
Realtime Updates
Phoenix offers a managed WebSocket channel for live leaderboard streams. Use it when you need immediate score changes in your game client, spectator dashboard, or broadcast overlays.
1. Endpoint & Authentication
GET /v1/leaderboards/{leaderboard_id}/ws
Authorization: Bearer <JWT>- Protocol: Secure WebSocket (
wss://). - Auth: Send the same JWT used for Query Gateway REST:
Authorization: Bearer <JWT>on the HTTP upgrade request. Tenant is resolved from the token; the path does not includetenant_id. - Phoenix validates the JWT and ensures the leaderboard exists for the tenant before upgrading the connection.
2. Connection Workflow
- Resolve leaderboard – ensure the leaderboard is active and you have the valid JWT for the tenant.
- Open WebSocket – use your preferred WebSocket client. On success, Phoenix immediately sends a snapshot for page 1 (size 100 by default).
- Send configuration – optionally send a
configuremessage to set custom pagination, filters, or window keys. - Stream updates – Phoenix delivers JSON messages as scores change, keeping your UI synchronized.
Connections idle for more than 30 seconds are pinged automatically; respond with pong frames or simply rely on Phoenix's built-in heartbeat messages.
3. Client Messages
To modify what you receive, send JSON messages with the following structure:
{
"type": "configure",
"page": 2,
"page_size": 50,
"filter": { "type": "top_n", "n": 100 }
}| Message Type | Fields | Purpose |
|---|---|---|
configure | page (optional), page_size (optional), filter (optional), window_key (optional) | Change the active page, size, filter strategy, or request a different time window. |
request_page | page (required), page_size (optional) | Fetch a specific page immediately without changing saved preferences. |
Filters (filter.type) mirror the options exposed by Phoenix's WebSocket handler:
none– receive all updates (default).page_range– only entries affecting a specific page.page_aware– current page plus users who might enter it (supportsbuffer_size).top_n– top N users regardless of pagination.user_ids– "watchlist" of specific users.rank_range– updates for a rank band (e.g., 1–50).
Phoenix acknowledges configuration changes by returning a config_ack message that echoes the active settings.
4. Server → Client Messages
All outbound messages are JSON with the type field shown below:
| Type | When it's sent | Payload Highlights |
|---|---|---|
snapshot | Immediately after connecting or when changing windows/filters. | Full page of entries, total counts, pagination metadata. |
page_data | Response to request_page. | Same shape as snapshot. |
score_update | Whenever a tracked user's score changes and passes your filter. | user_id, new_score, and optionally new_rank. |
config_ack | After Phoenix applies a configure request. | Confirms page, size, filter currently in effect. |
error | Invalid client request (malformed JSON, unknown filter type, etc.). | message describing the issue; connection stays open. |
ping | Heartbeat every ~30 seconds. | Timestamp for monitoring round-trip latency. |
All message schemas are documented in the services/query-gateway/src/handlers/websocket.rs file and remain stable for client integrations.
5. Filtering Strategies & Pagination Tips
- Top N overlays – use
filter: { "type": "top_n", "n": 50 }to power broadcast tickers without paginated requests. - Page-aware UIs –
page_awareensures you see anyone who might enter the current page (configurablebuffer_sizedefaults topage_size). - Watchlists – track specific users or squads by sending
user_ids. Phoenix only delivers updates for those IDs, reducing noise. - Rank range highlights – perfect for spotlighting mid-tier brackets ("ranks 500–550").
To keep clients in sync:
- Open WebSocket and display the initial
snapshot. - Listen for
score_updatemessages and patch your UI. - Periodically refresh via
request_pageto handle large jumps (e.g., a user moving multiple pages at once). - Handle
config_ackto confirm server-side pagination state matches your UI.
6. Handling Windows
By default, Phoenix streams the current window for the leaderboard's timeframe. If you need a completed window:
- Include
window_keyin yourconfiguremessage (e.g.,"window_key": "2025-11-18T12"for hourly). - If the requested window is historical, Phoenix sources data from its snapshot store and still emits updates (none for completed windows, but the page data is returned).
- If the window doesn't exist, you receive an
errormessage with details.
7. Error & Disconnect Scenarios
| Condition | Server Behavior | Recommended Client Action |
|---|---|---|
| Invalid signature/timestamp | Connection rejected with HTTP 401 before WebSocket upgrade. | Recalculate HMAC, ensure timestamp accuracy. |
| Tenant or leaderboard inactive | Connection rejected with HTTP 403/404. | Surface UI error; prompt operators to activate the leaderboard. |
| Malformed client message | Connection stays open; Phoenix sends error message. | Fix payload and resend. |
| Idle timeout / heartbeat failure | Phoenix closes the connection after repeated missed pings/pongs. | Reconnect automatically. |
| Internal server issue | Connection may drop; Phoenix logs the incident. | Apply exponential backoff before reconnect attempts. |
Always treat WebSocket connections as transient. Implement reconnection logic and resume from the latest known page/window.
8. Sample Client (TypeScript)
WebSocket auth uses the same JWT as Query REST: send Authorization: Bearer <JWT> on the HTTP upgrade request. Path does not include tenant_id (tenant from JWT).
import WebSocket from "ws";
const leaderboardId = "xp-weekly-global";
const jwt = "your-jwt-token";
const ws = new WebSocket(
`${QUERY_WS_URL}/v1/leaderboards/${leaderboardId}/ws`,
{
headers: {
Authorization: `Bearer ${jwt}`,
},
}
);
ws.on("open", () => {
console.log("Connected");
ws.send(
JSON.stringify({
type: "configure",
page: 1,
page_size: 50,
filter: { type: "page_aware", page: 1, page_size: 50, buffer_size: 50 },
})
);
});
ws.on("message", (data) => {
const msg = JSON.parse(data.toString());
switch (msg.type) {
case "snapshot":
case "page_data":
renderLeaderboard(msg.entries);
break;
case "score_update":
applyScoreUpdate(msg);
break;
case "ping":
ws.pong();
break;
case "error":
console.error("Server error:", msg.message);
break;
}
});9. Checklist
- Use JWT in
Authorization: Bearer <JWT>when opening the WebSocket. - Handle
snapshot,page_data,score_update, andpingmessages. - Implement reconnection logic with exponential backoff.
- Use filters to limit bandwidth (top N, page-aware, watchlists).
- Surface errors to operators when Phoenix rejects a connection.
Next guide: Querying Results, covering REST APIs for live pages, user-centric queries, and historical snapshots.
Creating Leaderboards
How to provision, update, and retire leaderboards via the Admin Gateway. Assumes you understand the configuration model from Leaderboard Overview.
Querying Results
Use the Query Gateway to pull leaderboard standings for UI surfaces, analytics, or partner APIs. Covers REST endpoints, authentication, pagination, window selection, and historical snapshots.