Real-time 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/{tenant_id}/{leaderboard_id}/ws?timestamp=<unix>&signature=<hmac>- Protocol: Secure WebSocket (
wss://). - Headers: Standard WebSocket headers plus any required by your environment.
- Query params:
timestamp– Unix time in seconds (UTC), must be within ±5 minutes of Phoenix servers.signature–hmac-sha256=<hex>generated with the tenant’s secret.
Canonical string for signing:
WS
/v1/leaderboards/{tenant_id}/{leaderboard_id}/ws
{timestamp}This mirrors the tenant HMAC scheme described in getting-started.md. Phoenix validates the signature and ensures the tenant + leaderboard are active before upgrading the connection.
2. Connection Workflow
- Resolve leaderboard – ensure the leaderboard is active and you have the tenant’s secret.
- Generate signature – build the canonical string above, sign with the tenant secret, and append
signatureandtimestampquery params. - 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)
import WebSocket from "ws";
const tenantId = "tenant-prod-001";
const leaderboardId = "xp-weekly-global";
const { timestamp, signature } = buildSignature(
TENANT_SECRET,
"WS",
`/v1/leaderboards/${tenantId}/${leaderboardId}/ws`,
"" + Math.floor(Date.now() / 1000)
);
const ws = new WebSocket(
`${QUERY_WS_URL}/v1/leaderboards/${tenantId}/${leaderboardId}/ws?timestamp=${timestamp}&signature=${encodeURIComponent(
signature
)}`
);
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;
}
});Implement your own buildSignature using the canonical string described earlier.
9. Checklist
- Generate tenant HMAC signatures for WebSocket URLs.
- 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
This guide teaches you how to provision, update, and retire leaderboards via the Admin Gateway.
Querying Results
Use the Query Gateway to pull leaderboard standings for UI surfaces, analytics, or partner APIs. This guide covers the REST endpoints, authentication, pagination, window selection, and historical snapshots.