API Docs

GameSDK progression & social reference

AdminUpdated Sep 22, 2026

GameSDK progression & social reference

This is the reference for the GameSDK methods that feed player progression (XP, levels, achievements) and social features (friends, profiles, the in-game leaderboard). For concepts, caps, how to declare achievements, and what players see, read XP, achievements & social first. For loading the SDK, ready(), start() and on(), see GameSDK core reference.

Common behaviour

These rules apply to every method on this page.

  • Runtimes. Available wherever window.GameSDK is loaded: HTML5 games that include the SDK script, and runtimes whose hosted shell includes it for you (see How games run). Calls work only while your game is running inside the Cool GPT Games player. Opened any other way, the page doesn't answer, so every method resolves its timeout fallback. - Signed-in vs guest. The player's sign-in state decides what happens. Guests always get the "guest" result listed for each method. Games embedded on other websites run as guests (see Embedding & the score bridge). - Play session. awardXp and unlockAchievement count only inside a live play session that has run for at least 30 seconds with at least 2 heartbeats. The session opens when your game calls GameSDK.start() (or GameSDK.replay.ready()). Without one, they resolve with the "nothing landed" result.

  • Never rejects, always settles. No method on this page rejects its promise or throws, and every promise has a built-in timeout. Failures come back as resolved values: zeros, empty arrays, unlocked: false with a reason, status: "error".

  • Message budget. The player page accepts at most 30 SDK messages per second from your game, across all methods. Extra messages are silently dropped, and a dropped request resolves its timeout fallback. - Replies are matched by request id. Each call gets the answer to its own request, so parallel and repeated calls are safe.

Method summary

Method

Resolves

Needs sign-in

Timeout / fallback

awardXp(amount, reason?)

{ granted, total, level, leveledUp }

Yes (guests get 0)

8 s → { granted: 0, total: 0, level: <last known>, leveledUp: false }

unlockAchievement(key)

{ key, name, alreadyHad, unlocked, reason? }

Yes (guests: reason: "sign_in_required")

8 s → unlocked: false, reason: "timeout"

getPlayer()

{ signedIn, level, handle }

No

5 s → last known player info

on(event, callback)

returns GameSDK

No

n/a

social.getFriends()

Array<{ handle, displayName, avatarUrl, level }>

Yes (guests get [])

6 s → []

social.addFriend(handle)

{ handle, status }

Yes (guests get "error")

6 s → { handle, status: "error" }

social.viewProfile(handle)

undefined (no promise)

No

n/a

social.getLeaderboard(scope?)

Array<{ rank, handle, displayName, avatarUrl, level, score }>

Only for "friends"

6 s → []


GameSDK.awardXp(amount, reason?)

awardXp(amount: number, reason?: string): Promise<{
  granted: number;
  total: number;
  level: number;
  leveledUp: boolean;
}>

Asks the platform to give the signed-in player XP for genuine gameplay. The platform decides how much actually lands.

Parameters

Name

Type

Required

Constraints

amount

number

yes

Converted with Number(amount), so non-numeric values become 0. Rounded down. <= 0 grants nothing. Values above 100 are cut to 100.

reason

string

no

Converted with String(). Up to 120 characters. Longer strings make the request fail (resolves as "nothing landed"). Shown to the player as the XP toast's subtitle. The first 100 characters are stored with the grant.

Resolved value

Field

Type

Meaning

granted

number

XP that actually landed. 0amount (after caps).

total

number

The player's lifetime XP after this grant.

level

number

The player's site-wide level after this grant.

leveledUp

boolean

true if this grant pushed the player up one or more levels.

Caps applied, in order: 100 per award → diminishing returns max(0.15, 1 − xpFromThisGameToday / 500) → 200 per game per session → 500 per game per player per UTC day → 3,000 per player per UTC day across the whole site. Achievement, first-play and personal-best XP from your game count toward the session and per-game daily totals. See XP, achievements & social for a worked example.

Guests and failures

Situation

Resolves

XP landed

{ granted: n, total, level, leveledUp }

All caps reached, or a non-positive amount

{ granted: 0, total: <real total>, level: <real level>, leveledUp: false }

Guest

{ granted: 0, total: 0, level: 1, leveledUp: false }, plus a one-time sign-in toast

No play session (start() not called)

{ granted: 0, total: 0, level: <last known>, leveledUp: false }

Session under 30 s / 2 heartbeats, ended, or flagged

{ granted: 0, total: 0, level: <last known>, leveledUp: false }

Rate-limited, invalid reason, or network/API error

{ granted: 0, total: 0, level: <last known>, leveledUp: false }

No answer within 8 s (for example, the request was dropped over 30 messages/s)

{ granted: 0, total: 0, level: <last known>, leveledUp: false }

<last known> is the player's level as last seen by the page or the SDK (from getPlayer() or an earlier awardXp answer), or 1 if it isn't known yet. In every "nothing landed" case except "caps reached", total is a placeholder 0, not the player's real total. Show total only when granted > 0. These placeholder answers never change the level the SDK remembers for getPlayer(). The SDK doesn't tell you why nothing landed.

Side effects

  • The xp event fires with the same object on every answer from the page, including granted: 0. It doesn't fire on the 8 s timeout.

  • If leveledUp, the levelUp event fires once with { level }.

  • The site shows a "+N XP" toast when granted > 0, and a "Level N!" toast on level-up. The player also gets a site notification and 100 bonus coins per level gained.

  • Every landed XP point also gives the player 1 coin (virtual currency).

Limits

  • 120 requests per minute per player per game. Over the limit, it resolves as "nothing landed".

  • The caps above.

  • Upload checks reject ungated grants on load or in setInterval, and flag grants in loops/timers and literal amounts of 10,000 or more for review.

Example

function onRoundWon(stars) {
  GameSDK.awardXp(10 + stars * 10, "Round won").then(function (r) {
    if (r.granted > 0) hud.flash("+" + r.granted + " XP");
    if (r.leveledUp) hud.flash("Level " + r.level + "!");
  });
}

Pitfalls

  • It can take up to 8 s to resolve if the page doesn't answer. Don't block gameplay on it.

  • Grants in the first ~30 s after start() return 0.

  • reason is visible to players, and over 120 characters the grant fails.


GameSDK.unlockAchievement(key)

unlockAchievement(key: string): Promise<{
  key: string;
  name: string | undefined;
  alreadyHad: boolean;
  unlocked: boolean;
  reason?: string;
}>

Unlocks one of your game's declared achievements for the signed-in player. It's safe to repeat: a player holds each achievement once. The XP reward is set by the achievement's tier (common 10, uncommon 25, rare 50, epic 100, legendary 200) and goes through the same caps as awardXp, including the 100-per-grant limit.

Parameters

Name

Type

Required

Constraints

key

string

yes

Converted with String(). Must match an active key declared for your game: 2–48 characters matching ^[a-z0-9][a-z0-9_]*$. Keys are case-sensitive.

Achievements are declared in a version's gamification block (sent with the upload-init or versions request, or through the CLI, publisher SDK or MCP server) and become active when that version is approved. See XP, achievements & social.

Resolved value

Field

Type

Meaning

key

string

The key you passed.

name

string | undefined

The achievement's display name. Set when the key matched an active achievement for a signed-in player in a valid session.

alreadyHad

boolean

true if the player had already unlocked it before this call.

unlocked

boolean

true only when this call newly unlocked it.

reason

string (optional)

Present only when neither unlocked nor alreadyHad is true: why nothing was unlocked.

Outcomes

Situation

Resolves

Toast

New unlock

{ key, name, alreadyHad: false, unlocked: true }

Achievement toast (plus level-up toast if one happened)

Already unlocked

{ key, name, alreadyHad: true, unlocked: false }

None

Unknown or deactivated key

{ key, name: undefined, alreadyHad: false, unlocked: false, reason: "unknown_achievement" }

None

Guest

unlocked: false, reason: "sign_in_required"

One-time sign-in toast

No play session (start() not called)

unlocked: false, reason: "no_session"

None

Session under 30 s / 2 heartbeats

unlocked: false, reason: "insufficient_play"

None

Session ended or flagged

unlocked: false, reason: "session_ended" or "session_flagged"

None

Rate-limited

unlocked: false, reason: "rate_limited"

None

Other API or network error

unlocked: false, reason: the API error code, or "error"

None

No answer within 8 s (for example, the request was dropped over 30 messages/s)

{ key, name: undefined, alreadyHad: false, unlocked: false, reason: "timeout" }

None

A new unlock is unlocked === true. Unlocking doesn't return the XP granted, but the levelUp event fires if the achievement's XP caused a level-up.

Side effects

  • The achievement event fires only when unlocked is true.

  • The unlock appears on the player's account page and in their public-profile showcase, and counts toward the site's daily "earn an achievement" quests.

Limits

  • 60 requests per minute per player per game.

  • At most 30 achievements per game.

  • A literal key in your code that the uploaded version doesn't declare gets the version rejected automatically. A version uploaded without a gamification block keeps the game's current achievements, so their keys count as declared.

Example

function onLevelComplete(level, hitsTaken, timeSec) {
  if (level === 1) GameSDK.unlockAchievement("first_clear");
  if (hitsTaken === 0) {
    GameSDK.unlockAchievement("untouchable").then(function (r) {
      if (r.unlocked) showBadgePopup(r.name);
    });
  }
  if (timeSec < 60) GameSDK.unlockAchievement("speedrunner");
}

Pitfalls

  • It can take up to 8 s to resolve if the page doesn't answer. Don't block gameplay on it.

  • A version approved with a gamification block replaces the game's achievement list: keys it leaves out are deactivated, and an empty achievements list deactivates them all. Unlocking a deactivated key resolves reason: "unknown_achievement". A version without a block leaves the list unchanged.

  • name can be set when nothing unlocked (the player already had it). Check unlocked to decide whether to celebrate.


GameSDK.getPlayer()

getPlayer(): Promise<{
  signedIn: boolean;
  level: number;
  handle: string | null;
}>

Returns who is playing: whether they're signed in, their site-wide level, and their handle.

Parameters

None.

Resolved value

Field

Type

Meaning

signedIn

boolean

Whether the player is signed in to Cool GPT Games.

level

number

Site-wide level (1–999). Always 1 for guests.

handle

string | null

The player's handle (without @), or null for guests.

Situation

Resolves

Signed in

{ signedIn: true, level: 7, handle: "alice" }

Guest

{ signedIn: false, level: 1, handle: null }

Signed in, but looking up progress or profile failed

{ signedIn: true, level: 1, handle: null } (either field can fall back separately)

No answer within 5 s

The SDK's last known player info (see pitfalls)

Side effects

The player event fires with the same object when the answer arrives.

Limits

None beyond the 30-messages-per-second budget. The page looks the player up once (two API requests) and then answers from memory. Parallel calls share that one lookup, and a failed lookup is retried on the next call.

Example

GameSDK.getPlayer().then(function (p) {
  if (p.signedIn) {
    title.textContent = "Welcome back" + (p.handle ? ", @" + p.handle : "") + "!";
    levelBadge.textContent = "Lv " + p.level;
  } else {
    title.textContent = "Playing as guest: sign in to earn XP";
  }
});

Pitfalls

  • The 5 s fallback returns the SDK's last known info. Before any answer, that's the guest shape. Don't treat a fallback as definitive.

  • No display name, avatar, XP or title is exposed. The remembered level is kept current by awardXp answers and level-ups, but call again (or listen to levelUp) if you display it.


GameSDK.on(event, callback)

on(event: string, callback: (payload: any) => void): typeof GameSDK

Subscribes to SDK events. Chainable. Exceptions thrown in your callback are caught and ignored. Events for this area:

Event

Payload

Fires

xp

{ granted, total, level, leveledUp }

After every awardXp answer from the page (including granted: 0). Not fired on the 8 s timeout.

levelUp

{ level }

Once per level, when awardXp or an achievement's XP causes a level-up.

achievement

{ key, name, alreadyHad: false, unlocked: true }

Only when an unlockAchievement call newly unlocked the achievement.

player

{ signedIn, level, handle }

When a getPlayer() answer arrives.

friendAdded

{ handle, status }

Only when addFriend succeeded (status is "outgoing" or "friends").

GameSDK
  .on("levelUp", function (e) { confetti(); showLevel(e.level); })
  .on("achievement", function (a) { log("Unlocked " + a.name); });

The full event list (pause, resume, ads and more) is in GameSDK core reference.


GameSDK.social.getFriends()

social.getFriends(): Promise<Array<{
  handle: string;
  displayName: string | null;
  avatarUrl: string | null;
  level: number;
}>>

Returns the signed-in player's accepted friends, most recently added first.

Parameters

None.

Resolved value

An array of friend objects:

Field

Type

Meaning

handle

string

Friend's handle. Use it with viewProfile.

displayName

string | null

Friend's display name, if set.

avatarUrl

string | null

URL of the friend's avatar image, if any.

level

number

Friend's site-wide level.

Pending requests (incoming or outgoing) aren't included. The list isn't paginated.

Situation

Resolves

Signed in

Array (may be empty)

Guest

[]

Network/API error

[]

No answer within 6 s

[]

Limits

None beyond the 30-messages-per-second budget.

Example

GameSDK.social.getFriends().then(function (friends) {
  friendsPanel.innerHTML = "";
  friends.forEach(function (f) {
    var li = document.createElement("li");
    li.textContent = (f.displayName || "@" + f.handle) + " · Lv " + f.level;
    li.onclick = function () { GameSDK.social.viewProfile(f.handle); };
    friendsPanel.appendChild(li);
  });
});

Pitfalls

  • An empty array can mean "guest", "no friends" or "error". Use getPlayer() to show the right empty state.

  • Display names come from players. Insert them with textContent, never innerHTML.


GameSDK.social.addFriend(handle)

social.addFriend(handle: string): Promise<{
  handle: string;
  status: "outgoing" | "friends" | "self" | "error";
}>

Sends a friend request from the signed-in player to handle. If handle had already sent this player a request, the two become friends immediately.

Parameters

Name

Type

Required

Constraints

handle

string

yes

Converted with String(). 1–40 characters of A–Z a–z 0–9 _ - (no @). Case-insensitive match.

Resolved value

Field

Type

Meaning

handle

string

The handle you passed.

status

string

One of the values below.

status

Meaning

"outgoing"

A request is pending from this player to handle, whether just sent or sent earlier.

"friends"

They're friends: already, or just now because a reciprocal request was accepted.

"self"

handle is the signed-in player's own handle. Nothing happened.

"error"

Nothing happened. See causes below.

Causes of "error": guest, invalid handle format, no such player, either player has blocked the other, over the per-load or hourly limit, network/API error, or no answer within 6 s.

Side effects

  • On "outgoing", the other player gets a site notification (and possibly an email): "@you sent you a friend request".

  • On a reciprocal accept, the original requester gets "@you accepted your friend request".

  • The site shows the player a toast: "Friend request sent to @handle" or "You're now friends with @handle".

  • Guests see the one-time sign-in toast.

  • The friendAdded event fires with the result only for "outgoing" and "friends".

Limits

Limit

Value

Past the limit

Per game load

8 requests (counted even when a request fails or targets yourself; invalid handles and guest calls aren't counted)

"error" until the player page is reloaded

Per player, site-wide

30 per hour

"error"

Example

addFriendBtn.onclick = function () {
  addFriendBtn.disabled = true;
  GameSDK.social.addFriend(row.handle).then(function (r) {
    addFriendBtn.textContent =
      r.status === "friends" ? "Friends ✓" :
      r.status === "outgoing" ? "Requested" :
      r.status === "self" ? "That's you" : "Try later";
    addFriendBtn.disabled = r.status !== "error";
  });
};

Pitfalls

  • Call it only after the player explicitly asks. Never auto-friend.

  • Take handles from getLeaderboard() / getFriends(). Typed handles often fail.

  • The 8-per-load budget is shared by all your calls, including failed ones.


GameSDK.social.viewProfile(handle)

social.viewProfile(handle: string): void

Opens the public profile of handle (https://coolgptgames.com/u/<handle>) in a new browser tab. The profile shows level ring and title, XP, achievement count, achievement showcase, friends, published games, and add-friend / follow buttons.

Parameters

Name

Type

Required

Constraints

handle

string

yes

1–40 characters of A–Z a–z 0–9 _ -. Anything else is silently ignored.

Return value

undefined. There's no promise and no confirmation. If the handle doesn't exist, the tab shows a not-found profile.

Guests and limits

Works for guests. Counts toward the 30-messages-per-second budget.

Example

leaderboardRow.addEventListener("click", function () {
  GameSDK.social.viewProfile(entry.handle);
});

Pitfalls

  • Call it directly inside a click/tap handler. Browsers may block new tabs that aren't opened in response to user input.

  • Don't call it on load or on a timer.


GameSDK.social.getLeaderboard(scope?)

social.getLeaderboard(scope?: "global" | "friends"): Promise<Array<{
  rank: number;
  handle: string;
  displayName: string | null;
  avatarUrl: string | null;
  level: number;
  score: number;
}>>

Returns your game's top scores so you can draw an in-game leaderboard with profile and add-friend actions. This page covers the social side. For how scores are submitted, validated and ranked, see Scores, leaderboards & anti-cheat.

Parameters

Name

Type

Required

Constraints

scope

string

no

"friends" ranks the signed-in player and their friends only. Any other value, or none, means "global".

Resolved value

Up to 50 rows, highest score first, each player's all-time best on the public board. rank starts at 1. avatarUrl is included even though some older examples leave it out.

Situation

Resolves

"global"

Array (anyone, including guests)

"friends", signed in

Array of the player plus friends who have a score

"friends", guest

[]

Network/API error, or no answer within 6 s

[]

Weekly/monthly and verified-only boards aren't available through this method. See Scores, leaderboards & anti-cheat.

Example

GameSDK.social.getLeaderboard("friends").then(function (rows) {
  rows.forEach(function (r) {
    addRow("#" + r.rank, r.displayName || "@" + r.handle, r.score, {
      onProfile: function () { GameSDK.social.viewProfile(r.handle); },
      onAdd: function () { GameSDK.social.addFriend(r.handle); },
    });
  });
});

Related

Was this page helpful?