Run a leaderboard

Rank members by points, hand out prizes when a week or campaign ends, and show standings in your app.

A leaderboard ranks your members by their points. For example:

  • "This week's top contributors", with prizes for the top three every Monday.
  • "All-time XP", so long-time members can see where they stand.
  • "Launch campaign race", between two fixed dates.
  • "Token holders", ranked by a score your own system measures.

Points from quests, referrals and your own server feed the board automatically. You never submit a score by hand.

For the model behind this guide, read Leaderboards and Points and ledgers. See Compose a campaign for a complete example.

What members see

  • The standings. Each row shows a rank, a name and a score. Equal scores share a rank, such as 1, 2, 2, 4, unless the board ranks whoever reached the score first.
  • Their own place. A signed-in member sees their own row and the members just above and below them.
  • How they earned it. Your app can list the point entries that make up a member's score.
  • A name, or an alias. Members appear under a stable alias, or under their chosen name and avatar when your project shows profiles.
  • Signed-in members only, unless you make a board public.

What you can do in Console

Boards themselves are defined in code. In Console you can:

Without codeWhere
Create, rename and archive point ledgersRewards → Point ledgers
Watch standings and see how a board is scoredLeaderboards, About this board
Exclude a member from a board, or reinstate themLeaderboards, select the member
Award, spend, adjust or reverse a member's pointsLeaderboards or the member's page
Review prize winners, then approve or reject the prizesLeaderboards, Prize settlements
Take back one winner's prizeLeaderboards, Revoke completion beside the winner
Get started with a coding agentLeaderboards, Copy prompt, before any board exists

Choose a point ledger

A ledger holds the balances a leaderboard ranks. Manage ledgers in Console under Rewards → Point ledgers:

ActionEffect
New ledgerCreates a ledger with a name and a description that explains what it tracks. Domino generates its fixed ID.
Edit ledgerChanges its name and description in a dialog without moving any points.
Archive ledgerHides it from new reward choices, after you give a reason. Existing rewards, balances and history stay available.
Unarchive ledgerOffers an archived ledger to new rewards again, after you give a reason.
Select its IDCopies the ID for use in code.

Each ledger lists the published quests that pay into it. Two ledgers can both be named "XP" and still keep separate balances and histories.

  • Change ledgers in test. Ledgers are definitions, so live refuses creating, editing or archiving them, from Console too. Make the change in test, or declare the ledger in code and deploy it to test, then promote test to live. See test and live.
  • Permissions. Creating, editing and archiving a ledger need publish permission; reading the picker needs read.
  • Audit. Console Audit history records creating and archiving a ledger. An edit appears as Points or leaderboard changed. Ledgers declared in relay.json appear as part of the release that publishes them.

Console member detail lists the member's balance in every ledger. Choose a ledger there to page through its history, award points, record a positive or negative adjustment, or reverse a positive award or adjustment. Point changes need points permission and always record the operator's reason.

Reference ledgers in code

Publish a leaderboard

Define a leaderboard in your project, add its module to relay.json, and deploy the project bundle. Leaderboards rank the project's existing point ledgers.

Use your hosted project's test environment while you build. Test points and rankings persist between domino dev sessions; restarting the app doesn't reset them. Publish the test configuration and check the rankings with real member accounts. The offline runtime has been removed.

Define the calculation

import { defineLeaderboard, points } from "@domino-sdk/relay/authoring";
import { community } from "../ledgers";

export default defineLeaderboard({
  key: "weekly",
  title: "This week's contributors",
  ledger: community,
  score: {
    kind: "earned",
    window: { kind: "calendar", period: "week" },
  },
  settle: {
    at: "window-end",
    ties: "shared",
    prizes: [
      { ranks: [1, 1], rewards: [points(community, 100)] },
      { ranks: [2, 3], rewards: [points(community, 50)] },
    ],
  },
});

This board ranks the points members earned this week. When the week ends, first place wins 100 points and second and third win 50, once an operator approves. See Choose what counts for scores and windows.

The week is the project calendar's. Set the project timezone once, in Console Settings → General or with "calendar": { "timeZone": "Europe/Budapest" } in relay.json.

Add the module to the manifest

{
  "leaderboards": [{ "entry": "src/weekly.ts" }]
}

Keep the existing connection and quest fields in relay.json.

Preview and publish to test

domino deploy --preview
domino deploy

The preview shows the definition and the first page of proposed standings, with the time it calculated them. Points keep arriving after the preview, so it doesn't freeze any results. The second command publishes to test.

Live publication

Publish to test directly with domino deploy --environment test. For live, the CLI prepares a request, and a person with publish permission approves it in Console. See reviewed live publication.

Change a published leaderboard

Every version of a board is kept. Console Leaderboards shows the standings and member controls for the selected project and environment.

Before any board is published there, the page offers ready-made requests instead, such as a weekly board or all-time standings. Choose Copy prompt and paste it into your coding agent; the prompt includes the project, environment and setup steps.

Choose what counts

ScoreRanks by
balanceThe member's whole balance. Spending lowers their rank. This is the default.
earnedPoints earned in the window: awards, adjustments up or down, and reversals. Spending doesn't count. You can limit it to some quests.
netEvery change in the window, including spending.

Earned and net scores take a window:

WindowExampleMeaning
all-time{ "kind": "all-time" }The default.
fixed{ "kind": "fixed", "start": 1790812800000, "end": 1793491200000 }Between two dates, in UTC epoch milliseconds; the start is included and the end isn't.
rolling{ "kind": "rolling", "days": 7 }The last seven 24-hour periods, including entries accepted at the moment of calculation.
calendar{ "kind": "calendar", "period": "day" }The current day, week or month of the project calendar; weeks start Monday. Boundaries follow the project timezone, including daylight-saving changes, and match the project's budgets.

Ranking rules

  • Quest awards count automatically. They count when they pay into the board's ledger. For example, a quest award of 100 community points adds 100 to the weekly board above when it falls in the current week. You don't submit a separate leaderboard score.
  • Points are whole numbers. Custom formulas and outside measurements become points before they reach Domino. Use a dedicated ledger when an outside system owns the total; see Rank an external score.
  • Ties share ranks by default. Equal scores share a rank, for example 1, 2, 2, 4, and tied members keep a stable order.
  • Or first to reach the score ranks higher. A board can instead order equal scores by when each member reached their score: their latest qualifying entry, earliest first, then by member ID. Every member who scored gets their own rank, such as 1, 2, 3, 4. On a balance or net board, a spend or a negative adjustment is a qualifying entry too, so it moves that time.
  • No points, no rank, by default. Members without a qualifying entry aren't ranked. A member who spends their balance down to zero stays ranked on a balance board.
  • Or list everyone. A board can also list members with no qualifying entry, at a score of 0. They share one rank after everyone who scored, even someone whose entries add up to 0 or less, and they never win prizes.
  • Points on hold still count. Points held for a redemption reduce what a member can spend, but they aren't spending.
  • Everyone is eligible by default. Every member of the project is eligible. A board can rank only the members of one group, such as those who enrolled. Group membership and points are separate records.

Settle prizes after a window

A board can hand out prizes when its window ends, such as every week or at the end of a campaign. Domino freezes the final standings, an operator checks them, and prizes go out only after approval.

Add settle to an earned or net board with a fixed or calendar window. at is "window-end". Prize ranges are inclusive, in order, and can't overlap.

import {
  defineLeaderboard,
  defineLedger,
  defineTieredReward,
  points,
  staffHandover,
  untracked,
} from "@domino-sdk/relay/authoring";

const community = defineLedger({ key: "community", name: "Community" });
const podium = defineTieredReward({
  key: "podium",
  title: "Podium prize",
  tiers: [{ key: "gold", title: "Gold" }],
  inventory: untracked(),
  fulfillment: staffHandover(),
});

export default defineLeaderboard({
  key: "weekly",
  title: "This week's contributors",
  ledger: community,
  score: {
    kind: "earned",
    window: { kind: "calendar", period: "week" },
  },
  settle: {
    at: "window-end",
    ties: "shared",
    prizes: [
      {
        ranks: [1, 1],
        rewards: [podium.tier("gold"), points(community, 100)],
      },
      { ranks: [2, 3], rewards: [points(community, 50)] },
    ],
  },
});
  • Prizes are points and tiered rewards. A prize can't repeat the same point ledger or tiered reward. Badges, group memberships and integration rewards, such as Discord roles, aren't supported: a badge or group prize makes defineLeaderboard throw, and a deployment refuse it, with a message naming the reward kind. Give those from a quest instead.
  • Only boards with an end. Balance, rolling and all-time boards can't settle.

Choose how ties are paid before you publish:

tiesResult
sharedEqual scores share a rank. Every member whose shared rank is in a prize range gets that prize.
earliestEqual scores are ordered by who reached their score first (their last qualifying entry), then by member ID. Every position has its own rank.

The standings members see before the cutoff follow the board's own ties, not settle.ties. Set ties: "earliest" on the board as well so the live order matches what the settlement freezes. When the two differ, including a board that sets only settle.ties: "earliest", defineLeaderboard prints a warning and domino check reports a LEADERBOARD_TIES_DIFFER warning that names the file. Neither stops the deploy.

At the end of the window, Domino freezes the board version active at that moment, every eligible ranked row, the exclusions, the ranks and the prizes each row wins. Points, group or exclusion changes and new board versions after that don't change what you review.

Split a pool by score

Add pool to settle to split a fixed amount over the eligible members in proportion to their scores. A settlement needs rank prizes, a pool, or both: with a pool, prizes can be empty, and defineLeaderboard lets you leave it out.

import { defineLeaderboard, defineLedger } from "@domino-sdk/relay/authoring";

const views = defineLedger({ key: "views", name: "Clip views" });

export default defineLeaderboard({
  key: "clip-contest",
  title: "Clip contest",
  ledger: views,
  score: {
    kind: "earned",
    window: { kind: "fixed", start: 1767225600000, end: 1769904000000 },
  },
  settle: {
    at: "window-end",
    ties: "shared",
    pool: {
      amount: "10000000000",
      unit: { code: "USDC", decimals: 6 },
      minScore: 100,
    },
  },
});
FieldMeaning
amountThe whole pool in base units, as a decimal string. "10000000000" is 10,000 USDC at 6 decimals. parseUnits("10000", 6).toString() from @domino-sdk/relay converts.
unit"points", or { code, decimals } for a token or currency (code up to 32 characters, 0–36 decimals).
ledgerThe point ledger a points pool pays into: a ledger handle or key. Required for "points", refused for any other unit.
minScoreOptional. Members scoring at or below it get no share. Members scoring 0 or less never get one.

A points pool holds at most 2,147,483,647 points. Each pool share rounds down, and the leftover base units go one each to the largest remainders, earlier positions first, so the shares sum exactly to the pool. See Pool prizes.

The settlement reads (settlementReview, settlement, and domino leaderboard-settlements get) carry the frozen pool with its allocated and unallocated totals, and each row's share in base units. Console shows each winner's pool share, formatted with the unit's decimals, before you approve.

Export the payout list

Relay pays a points pool on approval. For any other unit it only records the shares, and you pay them. In Console Leaderboards, select the window under Prize settlements and select Download payout list (CSV). The button appears for a pending or approved settlement whose pool is not points. From the CLI, export the same list:

domino leaderboard-settlements export SETTLEMENT > payouts.csv
domino leaderboard-settlements export SETTLEMENT --json

The CSV has one row per frozen member, with columns member, score, rank, share (base units), amount (whole units), unit, and wallets: the member's verified linked wallets as chain:address, separated by spaces, with the CAIP-2 network in place of the chain for Cosmos wallets. Wallets a member import attached without proof are left out. --json, and management.leaderboards.settlementExport(id) from a server, return the same rows with each wallet as { chain, network, address }. settlementPayoutCsv(result) from @domino-sdk/relay/server turns that result into the CSV the CLI prints. Reading the export requires read permission. A pending settlement's export shows what approval would make owed. A rejected settlement allocates nothing, so its export lists every share as 0 and the whole pool as unallocated, while its review keeps the frozen shares.

Review and approve delivery

Open Console Leaderboards, select the board and the window under Prize settlements, and compare the frozen winners and prizes. Enter a reason and select Approve and deliver. A confirmation states how many members the prizes reach, the points they pay per ledger, and the environment; select Approve and deliver again to deliver them. This needs review permission, and delivers every listed prize together.

Approval gives each winner one completion for their prize. Console shows the approval reason with who approved it and when.

Server operators can do the same review:

import { createManagementClient } from "@domino-sdk/relay/server";

declare const management: ReturnType<typeof createManagementClient>;

const windows = await management.leaderboards.settlements("weekly");
const latest = windows.at(0);

if (!latest) throw new Error("No ended window to review");

const frozen = await management.leaderboards.settlement(latest.id);

const approved = await management.leaderboards.approveSettlement(frozen.id, {
  actionId: `approve-${frozen.id}`,
  reason: "Eligibility and final scores verified",
});

Correct one winner

An approved settlement stays as the record of the review. To take back one winner's prize, use Revoke completion beside that winner in Console.

  • Points come back as far as they can. Domino reverses each point prize up to what the member hasn't spent or put on hold in that ledger, and shows what it couldn't reverse. It records no debt, so points the member already spent or reserved stay with them. Console keeps that remainder visible after a reload. Someone with points permission can open the linked point adjustment and record an explicit correction with its own reason.
  • Tiered prizes are corrected too. The winner's tiered-reward entitlement is updated.
  • No second payout. A leaderboard prize can't be freed and awarded again. Console keeps the winner in the settlement and marks the completion revoked.

A trusted backend can revoke the row's completion instead:

import { createManagementClient } from "@domino-sdk/relay/server";

declare const management: ReturnType<typeof createManagementClient>;

const approved = await management.leaderboards.settlement(
  "leaderboard_settlement_example",
);
const winner = approved.rows.find((row) => row.member === "member_alice");

if (winner?.completion) {
  await management.completions.revoke({
    actionId: `revoke-${winner.completion}`,
    completion: winner.completion,
    reason: "Member was not eligible at cutoff",
    freeOccurrence: false,
  });
}

Read standings in your app

Your app reads the boards and their standings, one page at a time:

import { createRelayClient } from "@domino-sdk/relay";

const domino = createRelayClient({ baseUrl: "/relay" });
const boards = await domino.leaderboards.list();
const page = await domino.leaderboards.standings("weekly", { limit: 25 });
const { items, me, nearby, period, calculatedAt, lastEntryAt } = page;
if (page.nextCursor) {
  const nextPage = await domino.leaderboards.standings("weekly", {
    cursor: page.nextCursor,
    limit: 25,
  });
}
FieldContains
itemsRanked rows. Render each item's rank, name and score, and use participant as its stable row key for this board. profile holds the name and avatar where the project shows profiles.
meThe signed-in member's row, or null when unranked or signed out. With includeUnscored, an eligible member always has a row.
nearbyUp to two competitors on either side, plus that member.
totalThe number of ranked members.
lastEntryAtWhen Domino received the latest balance entry, not when an outside provider last synced.
nextCursorThe cursor for the next page, when there is one.

Relay may add fields to a page and its rows in later releases, so read the ones you use and ignore the rest.

Show the returned rank, not the row's position, so ties display correctly. On a board with ties: "earliest" the rows come back already in first-to-reach order, each with its own rank, so one paged read gives the tie-broken board. Show an empty state when items is empty, and an unranked state when a signed-in member's me is null.

Show how a member earned their score

Your app can list the point entries that make up a signed-in member's score on one board, so they can see where it came from. Domino picks the entries with the board's own rules: spending is left out of an earned board, and imported opening balances count only on a balance board. Together the pages add up to the member's score, as long as they aren't excluded or removed from the board's group while you page.

import { createRelayClient } from "@domino-sdk/relay";

const domino = createRelayClient({ baseUrl: "/relay" });
const page = await domino.me.leaderboardHistory({ board: "weekly" });
const { items, period } = page;
if (page.nextCursor) {
  const older = await domino.me.leaderboardHistory({
    board: "weekly",
    cursor: page.nextCursor,
  });
}

Access and privacy

  • Signed-in members by default. Only signed-in members can read a board, unless you make it public. Signing out hides private boards.
  • Aliases unless you show profiles. Every row has a stable alias for that board. Where an operator makes member profiles visible, the row shows the member's chosen name and avatar instead; members who hid their profile keep the alias.
  • No private details. Rows never show internal member IDs, emails, account IDs or wallet addresses.
  • A member's place on a profile page. Your app can show one member's place on a board, applying the board's access and the profile's visibility. See compose a profile page.

Supply points from a trusted backend

Your own server can award points for things Domino doesn't see, such as purchases or event check-ins, and correct them. Those points count on leaderboards like any other. It writes with a management credential scoped to the project and environment:

Where the code runsCredential
A Domino-hosted app's own WorkerThe app's key, already bound as DOMINO_MANAGEMENT_KEY. See Call the management API from your server.
Any other backendA service key created in Console, kept in that backend's secret store.

In a hosted app's Worker, build the client from its bindings:

import { hostedManagementClient } from "@domino-sdk/relay/server";

export function management(env: Record<string, string | undefined>) {
  return hostedManagementClient(env);
}

Elsewhere, pass your own key to createManagementClient, as in the example below. Both clients have the same methods.

OperationRequired permission
Point updatespoints
Readsread
Custom tracker recordsingest

Editors and hosted-app keys have all three. ingest allows custom tracker records and nothing else. Reviewers and redemption staff can't change points. Every update needs an existing member of the project, an actionId and a reason. Reuse the same action ID and payload after a timeout; a changed payload with the same ID is rejected.

import { createManagementClient } from "@domino-sdk/relay/server";

const management = createManagementClient({
  baseUrl: "https://api.example.com",
  organization: "my-organization",
  project: "community",
  environment: "test",
  getToken: async () => "SERVER_SIDE_MANAGEMENT_KEY",
});

const award = await management.points.record({
  kind: "award",
  actionId: "external-activity-42",
  member: "member_existing",
  balance: "community",
  amount: 100,
  reason: "Verified external activity 42",
  effectiveAt: Date.parse("2026-09-01T12:00:00Z"),
});

await management.points.record({
  kind: "reverse",
  actionId: "activity-42-correction",
  member: "member_existing",
  balance: "community",
  entry: award.entry.id,
  amount: 20,
  reason: "Twenty units were attributed incorrectly",
});
kindTakesBehavior
awardA positive amountAdds points.
spendA positive amountSpends points. Spending can't increase a balance.
adjustA nonzero signed amountChanges earned scores.
reverseA positive amount and the entry ID of a positive award or adjustmentCan't exceed what's left unreversed. It takes the original entry's effective time and quest, so it corrects past earned scores.
settotal, expectedTotal, and classification: "earned" or "spend"Replaces the total. See Absolute updates.

Every entry also records when it was received and who made it.

Absolute updates

Read the current total with management.points.balance(member, balance) first. If another write changes it in between, the update fails with HTTP 409. Read again, decide again, and send a new action ID. An exact retry of a successful command returns its original result even if the balance has changed since.

Timing, limits, and history

  • Effective time. Without effectiveAt, an entry takes effect when it's received. Backdated entries are allowed; future dates are rejected.
  • No negative balances. A deduction can't make a balance negative or use points on hold. Sort out the conflicting spending or hold before applying a correction.
  • Journal. Read the append-only journal with management.points.history(member, balance, { cursor? }), 100 entries a page; pass a page's nextCursor back as cursor for the next.

Rank an external score

To rank something measured outside Domino, such as a rating from your game server or tokens held in members' wallets, give it its own ledger. Your server keeps that ledger equal to the score with absolute updates, every change is recorded, and a balance board ranks it.

import { defineLedger } from "@domino-sdk/relay/authoring";

export const tokenBalance = defineLedger({
  key: "token-balance",
  name: "Tokens",
  description: "Tokens held across linked wallets, in whole tokens. Never spent.",
});

Register this export under pointLedgers in relay.json, then publish a balance board over the handle: defineLeaderboard({ key: "token-balance", title: "Token holders", ledger: tokenBalance, score: { kind: "balance" }, access: "public" }). A score your server already knows needs only the set loop at the end of the example below.

For a balance, keep the samples in a wallet tracker and set the ledger from the tracker's current total. Reading the chain is up to you: supply readBalance(account), and the example samples every linked wallet through it.

import { caip10 } from "@domino-sdk/relay/schema";
import { hostedManagementClient } from "@domino-sdk/relay/server";

type Management = ReturnType<typeof hostedManagementClient>;

/**
 * Supplied by your app: one account's balance in base units, or null for an account you do not
 * read, such as one on another chain. `account` is a CAIP-10 account ID.
 */
declare function readBalance(account: string): Promise<{ amount: string; decimals: number } | null>;

export async function refreshBalance(env: Record<string, string | undefined>, member: string) {
  const management = hostedManagementClient(env);
  const accounts = (await management.members.get(member)).wallets
    .filter((wallet) => wallet.status === "linked")
    .map((wallet) => caip10(wallet));
  const observedAt = Date.now();
  const samples = await Promise.all(
    accounts.map(async (account) => ({ account, balance: await readBalance(account) })),
  );
  const records = samples.flatMap(({ account, balance }) =>
    balance ? [{ id: account, author: { id: account }, occurredAt: observedAt, observedAt, data: balance }] : [],
  );

  if (records.length) {
    await management.trackers.ingest("token-balance", {
      actionId: `token-balance-${member}-${observedAt}`,
      records,
    });
  }

  await setScore(management, member);
}

async function setScore(management: Management, member: string) {
  for (let attempt = 0; attempt < 2; attempt++) {
    // Sum what the tracker holds now, never the values just read: a slower refresh cannot win.
    const page = await management.trackers.records("token-balance", { member, limit: 100 });
    // Sum in base units at the finest precision any wallet reports, then convert to whole tokens once.
    const decimals = Math.max(0, ...page.items.map((record) => Number(record.data.decimals)));
    const units = page.items.reduce(
      (sum, record) =>
        sum + BigInt(String(record.data.amount)) * 10n ** BigInt(decimals - Number(record.data.decimals)),
      0n,
    );
    const total = Number(units / 10n ** BigInt(decimals));
    const watermark = Math.max(0, ...page.items.map((record) => record.observedAt));
    const { total: expectedTotal } = await management.points.balance(member, "token-balance");

    if (total === expectedTotal) {
      return;
    }

    try {
      await management.points.record({
        kind: "set",
        classification: "earned",
        member,
        balance: "token-balance",
        total,
        expectedTotal,
        actionId: `score-token-balance-${member}-${expectedTotal}-${total}-${watermark}`,
        reason: `token-balance total at ${new Date(watermark).toISOString()}`,
      });
      return;
    } catch (error) {
      // 409: another refresh changed the balance first. Read again once; the next refresh converges.
      if ((error as { status?: number }).status !== 409) {
        throw error;
      }
    }
  }
}
  • Your app chooses the chains. caip10(wallet) names Ethereum mainnet for an EVM wallet and Solana mainnet for a Solana wallet; pass the network you read, such as caip10(wallet, "eip155:8453"), to name another. A smart-contract wallet is named on the chain that proved it (provedOn), and caip10 throws for any other network: skip it when you read another chain. A Cosmos wallet's chain ID is the one it was linked under, which proves nothing: check the address prefix in readBalance and return null for chains you don't read.
  • The tracker is the source of truth. The score sums the tracker's current records for the member's linked wallets, so a refresh that finishes out of order still computes from the latest samples, and an unlinked wallet stops counting at the next refresh.
  • Compare-and-set. set with expectedTotal refuses a concurrent change with 409. The action ID names the inputs, so an exact retry replays and a new total gets a new entry.
  • Whole units. Scores are whole numbers, so the example sums base units and converts the total to whole tokens once, rounding down.
  • Earned, never spent. A decrease becomes a negative adjustment, which a balance board ranks correctly. Don't price rewards in a score ledger or pay it from quests; a Console adjustment is overwritten by the next refresh.
  • Current as of the last refresh. Refresh after sign-in, after a wallet link, and from a scheduled hosted-app route that pages through members. The scheduled route verifies Domino's signature and sends custom tracker records with the hosted app's key, so refreshes continue when members don't come back.

A hosted app's key belongs to one environment: the staged build's key works on test data and the live app's on live data. To refresh from any other server, use a Console management key with ingest, points and read.

Moderate participation

In Console Leaderboards, About this board beside the standings names the scoring method, Current balance, Points earned or Net points, and shows the board's ledger, period, audience, eligibility and ID.

Standings stay visible while the next page loads. Previous and Next appear once a board has more than one page, and wait until that page has loaded.

Select a member's row to open them beside the standings. To reach a member who isn't ranked, search by name, email or ID in Find a member above the standings and choose them from the results.

WhereActionEffect
EligibilityExclude from this board / Reinstate on this boardNeeds a Reason for eligibility change. Adds them to, or removes them from, the board's exclusion group. Their points don't change, and they stay listed under Excluded members until reinstated.
Excluded membersChange exclusion group, choose an Exclusion group and give a Reason, then SavePoints the board at another group, such as one several boards share, or back to This board's own group. Later standings and settlements leave out that group's members.
Eligibility, boards with a groupAdd to group / Remove from groupChanges membership of the board's group, with the same reason. Publishing the board created the group if it was missing.
Change pointsAward, Spend or Adjustment, then Apply pointsNeeds the points and a Reason for change. Console shows the new balance before you apply it and blocks a deduction larger than the available points.
Points historyReverse beside a positive award or adjustment, then Reverse pointsTakes back up to that entry's points, with a reason, in the entry's original period. Entries are newest first; Load older entries reads the next page.

A banned member is left out of every board's standings, and of any settlement that freezes while they are banned, without being added to an exclusion group. Unbanning them brings them back.

Exclusions and group membership can change in live. Changing which group a board excludes is a definition change, so live refuses it, like other definition changes: make it in test and promote.

What leaderboards do not do

A board without settle never awards prizes or finalizes results; only a board with settle freezes its window and pays prizes, after a reviewer approves them. A "top ten" reward built elsewhere must decide how it treats ties and when results become final, and can read standings through an authorized backend. No board finds members' outside accounts or collects activity from other services on its own: points reach it from quests, referrals and your server. Boards never pay tokens or money either: a pool in another unit is recorded for you to pay.

Referral bonuses

Referral programs can add inviter bonuses to the same ledger. Those points count on boards like other earned points. Awards from your own server earn bonuses only when they opt in, and reading a leaderboard never triggers a referral payout. Prize points earn no referral bonus.

Reject an incorrect settlement

Open the frozen settlement in Console and choose Reject settlement with a Rejection reason before anyone approves it. The window stays in history as rejected and pays no prizes. Rejection is final for that window: it can't create a new freeze. To correct a prize that's already approved, revoke that winner's completion instead.

Compose achievement displays

Leaderboard prizes grant points and tiered rewards. Badges come from quests or reviewers. Your app can show a member's badges next to their leaderboard results. See badges.

On this page