Skip to main content

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:

JSON10 lines
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}
FieldValues
encryptionKey32-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 / maxValueAcceptable score range — out-of-range scores are rejected
cooldownSecondsMinimum seconds between submissions per player
isIncrementaltrue if scores add up over time, false for per-session scores
apiKeyUsed 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.

JavaScript10 lines
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});

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):

JSON9 lines
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:

Bash8 lines
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

JSON7 lines
1{2  "success": true,3  "total": 1,4  "successCount": 1,5  "failureCount": 0,6  "errors": []7}

Status codes

CodeMeaning
200Processed — check errors for partial failures
400Invalid payload
401Missing / invalid X-API-Key
429Rate limit (1000 req / 60 s)
500Server error

Per-score error types

  • no-active-season — no leaderboard season is open
  • user-not-founduserId doesn't exist
  • privacy-disabled — the player opted out of leaderboards
  • validation — score out of range or timestamp invalid
  • internal-server-error — database/unknown error

Patterns

Submit from client, validate via cooldown

JavaScript13 lines
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

TypeScript19 lines
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}