LogoStacked
Client SDK

Getting Started

Complete tutorial for integrating the Client SDK into your application

This guide will walk you through integrating the Stacked Client SDK into your application from installation to claiming your first offer.


Installation

Install the SDK via npm:

npm install @pixels-online/pixels-client-js-sdk
dotnet add package PixelsOnline.Client.SDK

Step-by-Step Integration

Create a Token Provider Endpoint

The Client SDK requires a JWT token to authenticate with the Stacked API. You must create a server endpoint that generates this token using the Analytics SDK.

Security

Never expose your API key or client ID in client-side code. Always generate JWTs server-side using the Analytics SDK.

Server-side example:

import PixelsAnalytics from '@pixels-online/pixels-analytics-node-sdk';

const analytics = new PixelsAnalytics({
  apiKey: process.env.PIXELS_API_KEY!,
  clientId: process.env.PIXELS_CLIENT_ID!,
  env: 'test',
});

// Express endpoint example
app.post('/api/stacked-token', async (req, res) => {
  const playerId = req.user.id; // Your user ID from session/auth

  try {
    const token = await analytics.signJwt({playerId});
    res.json({ token });
  } catch (error) {
    res.status(500).json({ error: 'Failed to generate token' });
  }
});

Initialize the Client

Create and configure the OfferwallClient in your application:

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

const offerwallClient = new OfferwallClient({
  env: 'test', // Use 'live' for production

  // Token provider - fetches JWT from your server
  tokenProvider: async () => {
    const response = await fetch('/api/stacked-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
    });
    const data = await response.json();
    return data.token;
  },

  // Fallback image for rewards without images
  fallbackRewardImage: '/images/default-reward.png',
});
using PixelsOnline.Client.SDK;

var offerwallClient = new OfferwallClient(new OfferwallConfig
{
    Env = "test", // Use "live" for production

    // Token provider - fetches JWT from your server
    TokenProvider = async () =>
    {
        var response = await httpClient.PostAsync("/api/stacked-token", null);
        var data = await response.Content.ReadFromJsonAsync<TokenResponse>();
        return data.Token;
    },

    // Fallback image for rewards without images
    FallbackRewardImage = "/images/default-reward.png",
});

Connect to the Offerwall

If you didn't set autoConnect: true, manually initialize the connection:

try {
  await offerwallClient.initialize();
  console.log('Connected to Stacked!');
} catch (error) {
  console.error('Failed to connect:', error);
}
try
{
    await offerwallClient.InitializeAsync();
    Console.WriteLine("Connected to Stacked!");
}
catch (Exception ex)
{
    Console.WriteLine($"Failed to connect: {ex.Message}");
}

This method does three things:

  1. Fetches the JWT token using your tokenProvider
  2. Retrieves initial offers and player data
  3. Establishes SSE connection for real-time updates

Listen for Events

Subscribe to connection and offer events to update your UI:

// Listen for initial offers and refresh events
offerwallClient.events.on('refresh', ({ offers, player }) => {
  console.log('Received offers:', offers);
  console.log('Player data:', player);

  // Update your UI
  displayOffersInUI(offers);
  updatePlayerStats(player);
});

// Listen for new offers appearing in real-time
offerwallClient.events.on('offer_surfaced', ({ offer }) => {
  console.log('New offer surfaced:', offer);
  showOfferNotification(offer);
});

// Listen for connection events
offerwallClient.events.on('connected', ({ timestamp }) => {
  console.log('Connected at:', timestamp);
  showConnectionStatus('online');
});

offerwallClient.events.on('disconnected', ({ reason }) => {
  console.log('Disconnected:', reason);
  showConnectionStatus('offline');
});
// Listen for initial offers and refresh events
offerwallClient.Events.On<RefreshEventData>("refresh", (data) =>
{
    Console.WriteLine($"Received {data.Offers.Length} offers");
    Console.WriteLine($"Player ID: {data.Player.Data.PlayerId}");

    // Update your UI
    DisplayOffersInUI(data.Offers);
    UpdatePlayerStats(data.Player);
});

// Listen for new offers appearing in real-time
offerwallClient.Events.On<OfferSurfacedEventData>("offer_surfaced", (data) =>
{
    Console.WriteLine($"New offer surfaced: {data.Offer.Name}");
    ShowOfferNotification(data.Offer);
});

// Listen for connection events
offerwallClient.Events.On<ConnectedEventData>("connected", (data) =>
{
    Console.WriteLine($"Connected at: {data.Timestamp}");
    ShowConnectionStatus("online");
});

offerwallClient.Events.On<DisconnectedEventData>("disconnected", (data) =>
{
    Console.WriteLine($"Disconnected: {data.Reason}");
    ShowConnectionStatus("offline");
});

Event Types

See the Events documentation for a complete list of all available events.

Access Offers

Access the current offers from the store:

// Get all offers
const allOffers = offerwallClient.getOffers();

// Filter by status (via store)
const claimableOffers = offerwallClient.store.getClaimableOffers();
const activeOffers = offerwallClient.store.getActiveOffers();

// Get specific offer
const offer = offerwallClient.store.getOffer('instance-id');
// Get all offers
var allOffers = offerwallClient.GetOffers();

// Filter by status (via Store)
var claimableOffers = offerwallClient.Store.GetClaimableOffers();
var activeOffers = offerwallClient.Store.GetActiveOffers();

// Get specific offer
var offer = offerwallClient.Store.GetOffer("instance-id");

Claim Rewards

When a player completes an offer, claim the rewards:

async function claimOffer(instanceId) {
  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
});

Access Player Data

Get current player information:

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));
}
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}");
}