LogoStacked
Client SDK

Working with Offers

Access offers, player data, conditions, and rewards

Offers are the core of the Stacked platform - they represent tasks, achievements, or milestones that players can complete to earn rewards. This guide covers how to retrieve offers, check completion conditions, claim rewards, and display reward information in your game.


Accessing Offers

The SDK provides several methods to retrieve offers from the local store. All offer data is automatically synchronized via the real-time connection.

Get All Offers

Retrieve all offers available to the player.

// Get all offers
const allOffers = offerwallClient.getOffers();
console.log(`Total offers: ${allOffers.length}`);

// Iterate through offers
allOffers.forEach(offer => {
  console.log(`${offer.name}: ${offer.status}`);
});
// Get all offers
var allOffers = offerwallClient.GetOffers();
Console.WriteLine($"Total offers: {allOffers.Length}");

// Iterate through offers
foreach (var offer in allOffers)
{
    Console.WriteLine($"{offer.Name}: {offer.Status}");
}

Filter Offers by Status

Filter offers by their current status to show players relevant content.

// Get claimable offers
const claimableOffers = offerwallClient.store.getClaimableOffers();

// Get active offers (not expired, not claimed)
const activeOffers = offerwallClient.store.getActiveOffers();

// Get offers by specific status
const surfacedOffers = offerwallClient.store.getOffersByStatus('surfaced');
const viewedOffers = offerwallClient.store.getOffersByStatus('viewed');
// Get claimable offers
var claimableOffers = offerwallClient.Store.GetClaimableOffers();

// Get active offers (not expired, not claimed)
var activeOffers = offerwallClient.Store.GetActiveOffers();

// Get offers by specific status
var surfacedOffers = offerwallClient.Store.GetOffersByStatus(PlayerOfferStatus.Surfaced);
var viewedOffers = offerwallClient.Store.GetOffersByStatus(PlayerOfferStatus.Viewed);

Get Specific Offer

Retrieve a single offer by its instance ID.

const instanceId = 'instance-abc123';
const offer = offerwallClient.store.getOffer(instanceId);

if (offer) {
  console.log('Offer found:', offer.name);
  console.log('Status:', offer.status);
  console.log('Rewards:', offer.rewards);
} else {
  console.log('Offer not found');
}
var instanceId = "instance-abc123";
var offer = offerwallClient.Store.GetOffer(instanceId);

if (offer != null)
{
    Console.WriteLine($"Offer found: {offer.Name}");
    Console.WriteLine($"Status: {offer.Status}");
    Console.WriteLine($"Rewards: {offer.Rewards.Count}");
}
else
{
    Console.WriteLine("Offer not found");
}

Claiming Rewards

When an offer becomes claimable, the player can claim their rewards. The SDK handles the claim request and the server validates and distributes the rewards.

Basic Claim

Call claimReward() or ClaimRewardAsync() with the offer's instance ID.

async function claimOffer(instanceId: string) {
  const offer = offerwallClient.store.getOffer(instanceId);

  if (!offer) {
    console.error('Offer not found');
    return;
  }

  if (offer.status !== 'claimable') {
    console.error(`Offer is not claimable yet. Status: ${offer.status}`);
    return;
  }

  try {
    await offerwallClient.claimReward(instanceId);
    console.log('Rewards claimed successfully!');
    showSuccessMessage(`You claimed ${offer.rewards.length} rewards!`);
  } catch (error) {
    console.error('Failed to claim rewards:', error);
    showErrorMessage('Failed to claim rewards');
  }
}

// Listen for claim events
offerwallClient.events.on('offer_claimed', ({ instanceId }) => {
  console.log(`Offer claimed: ${instanceId}`);
  // Update UI to reflect claimed offer
});
async Task ClaimOffer(string instanceId)
{
    var offer = offerwallClient.Store.GetOffer(instanceId);

    if (offer == null)
    {
        Console.WriteLine("Offer not found");
        return;
    }

    if (offer.Status != "claimable")
    {
        Console.WriteLine($"Offer is not claimable yet. Status: {offer.Status}");
        return;
    }

    try
    {
        await offerwallClient.ClaimRewardAsync(instanceId);
        Console.WriteLine("Rewards claimed successfully!");
        ShowSuccessMessage($"You claimed {offer.Rewards.Count} rewards!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failed to claim rewards: {ex.Message}");
        ShowErrorMessage("Failed to claim rewards");
    }
}

// Listen for claim events
offerwallClient.Events.On<OfferClaimedEventData>("offer_claimed", (data) =>
{
    Console.WriteLine($"Offer claimed: {data.InstanceId}");
    // Update UI to reflect claimed offer
});

Claim Hooks

Use the beforeOfferClaim hook to add client-side validation before claiming. Use the afterOfferClaim hook to show reward animations or update your UI after successful claims.


Working with Player Data

The player object contains the current state of player data including currencies, levels, achievements, and custom game data. This data is used by offer conditions to determine eligibility.

Accessing Player Data

Retrieve the current player state from the local store.

const player = offerwallClient.getPlayer();

if (player) {
  // Access player snapshot
  const snapshot = player.snapshot;

  console.log('Player ID:', snapshot.playerId);
  console.log('Days in game:', snapshot.daysInGame);
  console.log('Login streak:', snapshot.loginStreak);
  console.log('Trust score:', snapshot.trustScore);

  // Access currencies
  const goldBalance = snapshot.currencies?.['cur_coins']?.balance ?? 0;
  console.log('Gold balance:', goldBalance);

  // Access levels
  const forestryLevel = snapshot.levels?.['forestry']?.level ?? 1;
  console.log('Forestry level:', forestryLevel);

  // Access achievements
  const achievements = snapshot.achievements ?? {};
  console.log('Achievements:', Object.keys(achievements).length);
}
var player = offerwallClient.GetPlayer();

if (player != null)
{
    // Access player snapshot
    var snapshot = player.Snapshot;

    Console.WriteLine($"Player ID: {snapshot.PlayerId}");
    Console.WriteLine($"Days in game: {snapshot.DaysInGame}");
    Console.WriteLine($"Login streak: {snapshot.LoginStreak}");
    Console.WriteLine($"Trust score: {snapshot.TrustScore}");

    // Access currencies
    var goldBalance = snapshot.Currencies?.GetValueOrDefault("cur_coins")?.Balance ?? 0;
    Console.WriteLine($"Gold balance: {goldBalance}");

    // Access levels
    var forestryLevel = snapshot.Levels?.GetValueOrDefault("forestry")?.Level ?? 1;
    Console.WriteLine($"Forestry level: {forestryLevel}");

    // Access achievements
    var achievements = snapshot.Achievements ?? new Dictionary<string, AchievementData>();
    Console.WriteLine($"Achievements: {achievements.Count}");
}

Player Structure

The player object contains two main parts:

  1. snapshot - Core player data tracked by Stacked (currencies, levels, achievements, etc.)
  2. data - Additional optional game-specific data with visual metadata

For complete type documentation, see API Types.

Refreshing Player Data

Manually trigger a refresh to get the latest offers and player data from the server.

// Manually refresh offers and player data
await offerwallClient.refreshOffersAndPlayer();

// Listen for refresh events
offerwallClient.events.on('refresh', ({ offers, player }) => {
  console.log(`Refreshed: ${offers.length} offers`);
  console.log('Player data updated:', player.snapshot.playerId);
});
// Manually refresh offers and player data
await offerwallClient.RefreshOffersAndPlayerAsync();

// Listen for refresh events
offerwallClient.Events.On<RefreshEventData>("refresh", (data) =>
{
    Console.WriteLine($"Refreshed: {data.Offers.Length} offers");
    Console.WriteLine($"Player data updated: {data.Player.Snapshot.PlayerId}");
});

Checking Completion Conditions

Completion conditions determine when a surfaced offer can be claimed. The server automatically validates these conditions, but the SDK provides utilities to check progress client-side for UI purposes (progress bars, completion indicators, etc.).

For detailed documentation on how completion conditions work, see Completion Conditions Concept.

Use the completion conditions utility to verify if a player has met the requirements to claim an offer and get detailed progress information.

import { meetsCompletionConditions } from '@pixels-online/pixels-client-js-sdk';

const player = offerwallClient.getPlayer();
const offer = offerwallClient.store.getOffer('instance-id');

if (player && offer?.completionConditions) {
  const result = meetsCompletionConditions({
    completionConditions: offer.completionConditions,
    completionTrackers: offer.completionTrackers,
    playerSnap: player.snapshot,
    addDetails: true, // Get detailed condition data
  });

  console.log('Can claim:', result.isValid);
  console.log('Condition details:', result.conditionData);

  // Show progress
  if (result.conditionData) {
    const progress = calculateProgress(result.conditionData);
    showProgressBar(progress);
  }
}
using PixelsOnline.Client.SDK;

var player = offerwallClient.GetPlayer();
var offer = offerwallClient.Store.GetOffer("instance-id");

if (player != null && offer != null)
{
    var result = Conditions.MeetsCompletionConditions(
        offer.CompletionConditions,
        offer.CompletionTrackers,
        player.Snapshot,
        addDetails: true // Get detailed condition data
    );

    Console.WriteLine($"Can claim: {result.IsValid}");

    // Show progress for each condition
    if (result.ConditionData != null)
    {
        foreach (var detail in result.ConditionData)
        {
            Console.WriteLine($"{detail.Kind}: {detail.Text}");
            Console.WriteLine($"  Met: {detail.IsMet}");
            if (detail.TrackerAmount.HasValue)
            {
                Console.WriteLine($"  Progress: {detail.TrackerAmount}");
            }
        }
    }
}

Displaying Rewards

Rewards can have custom names and images defined by your game's asset resolver. The SDK provides helper methods to resolve these assets automatically.

Resolving Reward Assets

Get resolved reward data with custom names and images from your assetResolver configuration.

// Resolve a single reward
const reward = offer.rewards[0];
const resolved = offerwallClient.assets.resolveReward(reward);

console.log('Name:', resolved.name);      // Custom name or default
console.log('Image:', resolved.image);    // Custom image or fallback
console.log('Amount:', resolved.amount);  // Original amount

// Resolve all offer rewards
const resolvedRewards = offerwallClient.assets.resolveOfferRewards(offer);

resolvedRewards.forEach(reward => {
  console.log(`${reward.amount}x ${reward.name}`);
  displayRewardCard({
    name: reward.name,
    image: reward.image,
    amount: reward.amount,
    kind: reward.kind,
  });
});
// Resolve a single reward
var reward = offer.Rewards[0];
var resolved = offerwallClient.Assets.ResolveReward(reward);

Console.WriteLine($"Name: {resolved.Name}");      // Custom name or default
Console.WriteLine($"Image: {resolved.Image}");    // Custom image or fallback
Console.WriteLine($"Amount: {resolved.Amount}");  // Original amount

// Resolve all offer rewards
var resolvedRewards = offerwallClient.Assets.ResolveOfferRewards(offer);

foreach (var rewardData in resolvedRewards)
{
    Console.WriteLine($"{rewardData.Amount}x {rewardData.Name}");
    DisplayRewardCard(new RewardCardData
    {
        Name = rewardData.Name,
        Image = rewardData.Image,
        Amount = rewardData.Amount,
        Kind = rewardData.Kind
    });
}