About yandex-js-sdk
An integration skill for JavaScript games published on Yandex Games. It covers player data, storage, monetization, leaderboards, lifecycle events, and platform features.
Key features
- 01SDK initialization and lifecycle handling
- 02Player authorization and cloud storage
- 03Interstitial, rewarded, and banner ads
- 04In-app purchases
- 05Leaderboards and remote configuration
- 06Review prompts and game discovery
Use cases
- 01Adding Yandex player profiles and saved data
- 02Managing ads during gameplay
- 03Offering in-game purchases
- 04Submitting scores and reading remote flags
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ hungify/ playables-skills --skill yandex-js-sdkAfter installing, type/yandex-js-sdkin Claude Code to confirm it loaded.
Details
From the project READMEYandex Games JavaScript SDK
Overview
Yandex Games loads games inside a frame on yandex.<tld>/games. The host injects the YaGames global once the SDK script is loaded. Key capability areas:
- Lifecycle & environment: init,
LoadingAPI.ready(),GameplayAPI.start/stop, pause/resume events, device info, language/TLD - Player & storage: authorization, profile data, per-player key/value cloud storage
- Monetization: fullscreen interstitial, rewarded video, sticky banner ads, in-app purchases
- Engagement: leaderboards, remote config flags, review prompt, add-to-home-screen shortcut, server time, clipboard write, cross-promotion via
GamesAPI
The SDK does NOT expose social share, invite-friends, community-join, add-to-favorites, achievements, or stat counters.
Installation
<script src="https://yandex.ru/games/sdk/v2"></script>
If loaded dynamically, poll until window.YaGames && window.YaGames.init is defined before calling init().
Quick Start / Integration Workflow
Follow this sequence when wiring up the SDK for the first time. Verify each step before proceeding to the next.
async function bootstrap() {
// 1. Ensure SDK script has loaded
if (!window.YaGames?.init) {
throw new Error('YaGames SDK not available — check that the script tag loaded');
}
// 2. Initialize — all subsequent SDK calls require ysdk
const ysdk = await window.YaGames.init();
// 3. Bind host-driven pause / resume before anything else
ysdk.on('game_api_pause', () => {
/* mute audio, pause gameplay */
});
ysdk.on('game_api_resume', () => {
/* unmute, resume */
});
// 4. Signal that the loading screen is gone
ysdk.features.LoadingAPI?.ready();
// 5. Read environment (language, device type)
const env = readEnvironment(ysdk);
// 6. Fetch player — fall back to guest mode if not authorized
let player = null;
let storageCache = {};
try {
const info = await getPlayer(ysdk);
player = info.raw;
if (info.isAuthorized) {
storageCache = await loadStorage(player);
}
} catch (err) {
console.warn('Player init failed, continuing as guest:', err);
}
// 7. Start gameplay tracking
onGameplayStarted(ysdk);
return { ysdk, player, storageCache, env };
}
function readEnvironment(ysdk) {
return {
language: ysdk.environment.i18n.lang.toLowerCase(),
tld: ysdk.environment.i18n.tld.toLowerCase(),
deviceType: ysdk.deviceInfo.type // 'desktop' | 'mobile' | 'tablet' | 'tv'
};
}
Player / Authorization
async function getPlayer(ysdk, { useSignedData = false } = {}) {
const player = await ysdk.getPlayer({ signed: useSignedData });
return {
raw: player,
id: player.getUniqueID(),
name: player.getName() || null,
photos: ['small', 'medium', 'large'].map((size) => player.getPhoto(size)).filter(Boolean),
isAuthorized: player.isAuthorized(),
payingStatus: player.getPayingStatus(),
signature: useSignedData ? player.signature : null
};
}
async function authorizePlayer(ysdk, options) {
const current = await getPlayer(ysdk, options);
if (current.isAuthorized) {
return current;
}
await ysdk.auth.openAuthDialog();
return getPlayer(ysdk, options);
}
Storage
Every call writes the full document — there is no per-key API. Cache the latest snapshot client-side and merge before each save. Fall back to localStorage for guests.
async function loadStorage(player) {
return player.getData();
}
async function setStorage(player, cache, key, value) {
const data = { ...(cache || {}) };
if (Array.isArray(key)) {
for (let i = 0; i < key.length; i++) {
data[key[i]] = value[i];
}
} else {
data[key] = value;
}
await player.setData(data);
return data;
}
async function deleteStorage(player, cache, key) {
const data = { ...(cache || {}) };
if (Array.isArray(key)) {
for (let i = 0; i < key.length; i++) {
delete data[key[i]];
}
} else {
delete data[key];
}
await player.setData(data);
return data;
}
function getCached(cache, key) {
if (Array.isArray(key)) {
return key.map((k) => (typeof cache?.[k] === 'undefined' ? null : cache[k]));
}
return typeof cache?.[key] === 'undefined' ? null : cache[key];
}
Advertisement
Fullscreen interstitial
onClose(wasShown) fires on both normal close and SDK-skipped ads. Treat wasShown === false as a failure.
function showInterstitial(ysdk, handlers = {}) {
ysdk.adv.showFullscreenAdv({
callbacks: {
onOpen: () => handlers.onOpen?.(),
onClose: (wasShown) => handlers.onClose?.(wasShown),
onError: (err) => handlers.onError?.(err)
}
});
}
Rewarded video
Grant the reward inside onRewarded. onClose fires after the ad UI is dismissed.
function showRewarded(ysdk, handlers = {}) {
ysdk.adv.showRewardedVideo({
callbacks: {
onOpen: () => handlers.onOpen?.(),
onRewarded: () => handlers.onRewarded?.(),
onClose: () => handlers.onClose?.(),
onError: (err) => handlers.onError?.(err)
}
});
}
Sticky banner
async function getBannerStatus(ysdk) {
return ysdk.adv.getBannerAdvStatus(); // { stickyAdvIsShowing: boolean, reason?: string }
}
async function showBanner(ysdk) {
const data = await ysdk.adv.showBannerAdv();
return data.stickyAdvIsShowing === true;
}
async function hideBanner(ysdk) {
const data = await ysdk.adv.hideBannerAdv();
return data.stickyAdvIsShowing === false;
}
Payments / IAP
Obtain the payments object once after init. Pass signed: true for backend signature validation.
async function initPayments(ysdk, { signed = true } = {}) {
return ysdk.getPayments({ signed });
}
Catalog
async function getCatalog(payments) {
const products = await payments.getCatalog();
return products.map((p) => ({
id: p.id,
title: p.title,
description: p.description,
imageURI: p.imageURI,
price: p.price,
priceValue: p.priceValue,
priceCurrencyCode: p.priceCurrencyCode,
priceCurrencyImage: p.getPriceCurrencyImage?.('medium')
}));
}
Purchase
async function purchase(payments, productId, payload) {
const result = await payments.purchase({ id: productId, developerPayload: payload });
if (!result?.purchaseData?.purchaseToken) {
throw new Error(
`Purchase succeeded but returned no purchaseToken for product ${productId}`
);
}
return { id: productId, ...result.purchaseData };
}
Owned purchases
async function getPurchases(payments) {
const list = await payments.getPurchases();
return list.map((purchase) => ({ id: purchase.productID, ...purchase.purchaseData }));
}
Consume
Verify the item was granted before consuming. A failed consume leaves the token intact for retry.
async function consumePurchase(payments, purchaseToken) {
if (!purchaseToken) {
throw new Error('consumePurchase: purchaseToken is required');
}
await payments.consumePurchase(purchaseToken);
}
Leaderboards
Boards are configured in the developer console and referenced by string id. Setting a score requires an authorized player.
async function setLeaderboardScore(ysdk, leaderboardId, score) {
const lb = await ysdk.getLeaderboards();
await lb.setLeaderboardScore(leaderboardId, score);
}
async function getLeaderboardEntries(ysdk, leaderboardId, { isPlayerAuthorized = false } = {}) {
const lb = await ysdk.getLeaderboards();
const options = { quantityTop: 20 };
if (isPlayerAuthorized) {
options.includeUser = true;
options.quantityAround = 3;
}
const result = await lb.getLeaderboardEntries(leaderboardId, options);
if (!result || result.entries.length === 0) {
return [];
}
return result.entries.map((e) => ({
id: e.player.uniqueID,
name: e.player.publicName,
score: e.score,
rank: e.rank,
photo: e.player.getAvatarSrc('large')
}));
}
Remote config
ysdk.getFlags(options) returns a flat { key: stringValue } map.
async function getRemoteConfig(ysdk, options = {}) {
const params = { clientFeatures: [], ...options };
return ysdk.getFlags(params);
}
Server time
function getServerTime(ysdk) {
// Returns Unix timestamp in milliseconds
return ysdk.serverTime();
}
Social
Review prompt
async function requestReview(ysdk) {
const can = await ysdk.feedback.canReview();
if (!can.value) {
throw new Error(can.reason || 'review not available');
}
const { feedbackSent } = await ysdk.feedback.requestReview();
return feedbackSent;
}
Add to home screen (shortcut)
async function canAddToHomeScreen(ysdk) {
const prompt = await ysdk.shortcut.canShowPrompt();
return prompt.canShow === true;
}
async function addToHomeScreen(ysdk) {
const result = await ysdk.shortcut.showPrompt();
return result.outcome === 'accepted';
}
Clipboard
async function clipboardWrite(ysdk, text) {
await ysdk.clipboard.writeText(text);
}
Gameplay lifecycle, pause and audio
Bracket every active gameplay session with GameplayAPI.start() / GameplayAPI.stop() to drive ad pacing and analytics.
function onGameplayStarted(ysdk) {
ysdk.features.GameplayAPI?.start();
}
function onGameplayStopped(ysdk) {
ysdk.features.GameplayAPI?.stop();
}
function bindPauseHandlers(ysdk, { onPause, onResume }) {
ysdk.on('game_api_pause', () => onPause?.());
ysdk.on('game_api_resume', () => onResume?.());
}
Visibility/focus changes not announced by Yandex should still be handled with document.visibilitychange, window.blur, and window.focus alongside these SDK callbacks.
Catalog of other Yandex Games (optional)
features.GamesAPI is gated on availability per game; check for the namespace before calling it.
async function getAllGames(ysdk) {
const { games } = await ysdk.features.GamesAPI.getAllGames();
return games;
}
async function getGameById(ysdk, gameId) {
const { game, isAvailable } = await ysdk.features.GamesAPI.getGameByID(gameId);
return { ...game, isAvailable };
}