Leaderboards
Funox leaderboards rank player scores across all players. You can submit scores from the client SDK (easy, but trust-the-client) or via the server API (recommended if you have a backend).
Configuration
Leaderboards are configured per-game in the Developer Portal:
1{2 "encryptionKey": "<32-byte base64 string>",3 "scoreLabel": "POINTS",4 "scoreSorting": "DESC",5 "minValue": 0,6 "maxValue": 500000,7 "cooldownSeconds": 10,8 "isIncremental": false,9 "apiKey": "<server-side API key>"10}| Field | Values |
|---|---|
encryptionKey | 32-byte base64 string. Generated in the Developer Portal. |
scoreLabel | "XP" | "KDA" | "POINTS" | "MINUTES" |
scoreSorting | "ASC" (lower is better, e.g., time trials) or "DESC" (higher is better) |
minValue / maxValue | Acceptable score range — out-of-range scores are rejected |
cooldownSeconds | Minimum seconds between submissions per player |
isIncremental | true if scores add up over time, false for per-session scores |
apiKey | Used by the server-side leaderboard API |
Client-side: submitScore(...)
Scores submitted from the client are encrypted with AES-GCM before transmission to make tampering harder. Funox provides an encryptScore helper.
1const finalScore = 1520;2const encrypted = await window.Funox.SDK.leaderboards.encryptScore(3 finalScore,4 encryptionKey5);6 7await window.Funox.SDK.leaderboards.submitScore({8 encryptedScore: encrypted,9 score: finalScore,10});Client scores are not trusted
A determined player can still cheat. For competitive games, submit from your backend instead.
Server-side: Leaderboard API
POST https://leaderboard.funox.com/scores
Headers:
X-API-Key: <your apiKey>Content-Type: application/json
Body (max 100 scores per request):
1{2 "scores": [3 {4 "userId": "<from verified Funox token>",5 "score": 1520,6 "timestamp": "2026-05-16T12:00:00.000Z"7 }8 ]9}Example:
1curl -X POST https://leaderboard.funox.com/scores \2 -H "X-API-Key: $FUNOX_LEADERBOARD_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{5 "scores": [6 { "userId": "u_abc123", "score": 1520, "timestamp": "2026-05-16T12:00:00.000Z" }7 ]8 }'Response
1{2 "success": true,3 "total": 1,4 "successCount": 1,5 "failureCount": 0,6 "errors": []7}Status codes
| Code | Meaning |
|---|---|
| 200 | Processed — check errors for partial failures |
| 400 | Invalid payload |
| 401 | Missing / invalid X-API-Key |
| 429 | Rate limit (1000 req / 60 s) |
| 500 | Server error |
Per-score error types
no-active-season— no leaderboard season is openuser-not-found—userIddoesn't existprivacy-disabled— the player opted out of leaderboardsvalidation— score out of range or timestamp invalidinternal-server-error— database/unknown error
Patterns
Submit from client, validate via cooldown
1async function onGameOver(finalScore) {2 if (Date.now() - lastSubmit < 10_000) return; // respect cooldown3 lastSubmit = Date.now();4 5 const encrypted = await window.Funox.SDK.leaderboards.encryptScore(6 finalScore,7 LEADERBOARD_KEY8 );9 await window.Funox.SDK.leaderboards.submitScore({10 encryptedScore: encrypted,11 score: finalScore,12 });13}Submit from your server, validate the user
1// On your backend:2async function submitScore(userToken: string, score: number) {3 const user = await verifyFunoxToken(userToken); // validates JWT4 5 await fetch("https://leaderboard.funox.com/scores", {6 method: "POST",7 headers: {8 "X-API-Key": process.env.FUNOX_LEADERBOARD_API_KEY!,9 "Content-Type": "application/json",10 },11 body: JSON.stringify({12 scores: [{13 userId: user.userId,14 score,15 timestamp: new Date().toISOString(),16 }],17 }),18 });19}