LogoStacked
Analytics SDK

Event Tracking

Track user events to build profiles and trigger targeted offers

Overview

Event tracking is the foundation of Stacked's intelligent offer system. By tracking user actions and state changes, Stacked builds comprehensive user profiles that enable:

  • Targeted Offer Surfacing - Show offers to the right users at the right time
  • Completion Tracking - Monitor progress toward offer requirements
  • User Segmentation - Group users by behavior and characteristics
  • Analytics & Insights - Understand user behavior and optimize engagement

Important

Accurate event tracking is critical for Stacked to work effectively. Track events atomically and in real-time for best results.

Core Events

User Lifecycle Events

signUp

Track when a new user creates an account.

analytics.signUp('user-123', {
  platform: 'email',
  platform_identifier: 'user@example.com',
  username: 'PlayerOne'
});
err := analytics.SignUp(ctx, "user-123", pixels.SignUpParams{
    Platform:           "email",
    PlatformIdentifier: "user@example.com",
    Username:           stringPtr("PlayerOne"),
})

Parameters: See PixelsSignUpParams for full parameter details.


signIn

Track user login events.

analytics.signIn('user-123', {
  username: 'PlayerOne'
});
err := analytics.SignIn(ctx, "user-123", pixels.SignInParams{
    Username: stringPtr("PlayerOne"),
})

Parameters: See PixelsSignInParams for full parameter details.


Economy Events

spendCurrency

Track when users spend in-game currency. This is crucial for offer completion tracking.

const txId = analytics.spendCurrency('user-123', {
  currency_id: 'gold',
  currency_amount: 500,
  context: 'weapon_upgrade',
  to_kind: 'store',
  to_id: 'blacksmith',
  in_game_balance: 1500 // remaining balance
});
txID, err := analytics.SpendCurrency(ctx, "user-123", pixels.SpendCurrencyParams{
    CurrencyID:     "gold",
    CurrencyAmount: 500.0,
    Context:        stringPtr("weapon_upgrade"),
    ToKind:         stringPtr("store"),
    ToID:           stringPtr("blacksmith"),
    InGameBalance:  floatPtr(1500.0),
})

Parameters: See PixelsSpendCurrencyParams for full parameter details.

Completion Tracking

The spendCurrency event is commonly used in offer completion conditions. Make sure to track all purchases accurately.


earnCurrency

Track when users gain currency through gameplay or rewards.

analytics.earnCurrency('user-123', {
  currency_id: 'gold',
  currency_amount: 100,
  context: 'quest_complete',
  from_kind: 'player',
  from_id: 'quest_npc_01'
});
_, err := analytics.EarnCurrency(ctx, "user-123", pixels.EarnCurrencyParams{
    CurrencyID:     "gold",
    CurrencyAmount: 100.0,
    Context:        stringPtr("quest_complete"),
    FromKind:       stringPtr("player"),
    FromID:         stringPtr("quest_npc_01"),
})

Parameters: See PixelsEarnCurrencyParams for full parameter details.


depositCurrency

Track when users add currency to their account (e.g., purchases with real money).

analytics.depositCurrency('user-123', {
  currency_id: 'gems',
  currency_amount: 1000,
  context: 'iap_purchase',
  from_kind: 'store',
  from_id: 'apple_app_store'
});
_, err := analytics.DepositCurrency(ctx, "user-123", pixels.DepositCurrencyParams{
    CurrencyID:     "gems",
    CurrencyAmount: 1000.0,
    Context:        stringPtr("iap_purchase"),
    FromKind:       stringPtr("store"),
    FromID:         stringPtr("apple_app_store"),
})

Parameters: See PixelsDepositCurrencyParams for full parameter details.


withdrawCurrency

Track when users withdraw or convert currency.

analytics.withdrawCurrency('user-123', {
  currency_id: 'tokens',
  currency_amount: 50,
  context: 'crypto_withdrawal',
  to_kind: 'marketplace',
  to_id: 'external_wallet'
});
_, err := analytics.WithdrawCurrency(ctx, "user-123", pixels.WithdrawCurrencyParams{
    CurrencyID:     "tokens",
    CurrencyAmount: 50.0,
    Context:        stringPtr("crypto_withdrawal"),
    ToKind:         stringPtr("marketplace"),
    ToID:           stringPtr("external_wallet"),
})

Parameters: See PixelsWithdrawCurrencyParams for full parameter details.


Item Management Events

gainItem

Track when users acquire items.

analytics.gainItem('user-123', {
  item_id: 'sword_legendary_01',
  item_amount: 1,
  context: 'boss_defeat',
  from_kind: 'player',
  from_id: 'dungeon_boss'
});
err := analytics.GainItem(ctx, "user-123", pixels.GainItemParams{
    ItemID:     "sword_legendary_01",
    ItemAmount: 1,
    Context:    stringPtr("boss_defeat"),
    FromKind:   stringPtr("player"),
    FromID:     stringPtr("dungeon_boss"),
})

Parameters: See PixelsGainItemParams for full parameter details.


loseItem

Track when users lose or consume items.

analytics.loseItem('user-123', {
  item_id: 'health_potion',
  item_amount: 3,
  context: 'combat_use',
  to_kind: 'player',
  to_id: 'combat_system'
});
err := analytics.LoseItem(ctx, "user-123", pixels.LoseItemParams{
    ItemID:     "health_potion",
    ItemAmount: 3,
    Context:    stringPtr("combat_use"),
    ToKind:     stringPtr("player"),
    ToID:       stringPtr("combat_system"),
})

Parameters: See PixelsLoseItemParams for full parameter details.


gainManyItems

Track multiple item gains in a single event.

analytics.gainManyItems('user-123', {
  items: [
    { id: 'wood', amount: 10 },
    { id: 'stone', amount: 5 },
    { id: 'iron_ore', amount: 3 }
  ],
  context: 'resource_gathering',
  bundle_id: 'starter_pack'
});
err := analytics.GainManyItems(ctx, "user-123", pixels.GainManyItemsParams{
    Items: []pixels.ItemData{
        {ID: "wood", Amount: 10},
        {ID: "stone", Amount: 5},
        {ID: "iron_ore", Amount: 3},
    },
    Context:  stringPtr("resource_gathering"),
    BundleID: stringPtr("starter_pack"),
})

Parameters: See PixelsGainManyItemsParams for full parameter details.


Progression Events

levelUp

Track user level increases.

analytics.levelUp('user-123', {
  skill_id: 'combat',
  level_value: 15
});
err := analytics.LevelUp(ctx, "user-123", pixels.LevelUpParams{
    SkillID: "combat",
    Level:   15,
})

Parameters: See PixelsLevelUpParams for full parameter details.


gainAchievement

Track achievement unlocks.

analytics.gainAchievement('user-123', {
  achievement_id: 'first_boss_defeated',
  count: 1
});
count := 1.0
err := analytics.GainAchievement(ctx, "user-123", pixels.GainAchievementParams{
    AchievementID: "first_boss_defeated",
    Count:         &count,
})

Parameters: See PixelsGainAchievementParams for full parameter details.


State Management

playerSnapshot

Send a complete snapshot of the user's current state. This is crucial for offer targeting.

analytics.playerSnapshot('user-123', {
  username: 'PlayerOne',
  trust_score: 85,
  currencies: [
    { id: 'gold', balance: 1500, in: 5000, out: 3500 },
    { id: 'gems', balance: 50 }
  ],
  levels: [
    { skill_id: 'combat', level_value: 15 },
    { skill_id: 'crafting', level_value: 10 }
  ],
  achievements: [
    { id: 'first_boss', count: 1 },
    { id: 'pvp_wins', count: 25 }
  ],
  extra: {
    guild_id: 'warriors_123',
    premium_member: true
  }
});
trustScore := 85
err := analytics.PlayerSnapshot(ctx, "user-123", pixels.SnapshotParams{
    Username:   stringPtr("PlayerOne"),
    TrustScore: &trustScore,
    Currencies: []pixels.CurrencySnapshot{
        {ID: "gold", Balance: 1500, In: intPtr(5000), Out: intPtr(3500)},
        {ID: "gems", Balance: 50},
    },
    Levels: []pixels.LevelSnapshot{
        {SkillID: "combat", Level: 15},
        {SkillID: "crafting", Level: 10},
    },
    Achievements: []pixels.AchievementSnapshot{
        {ID: "first_boss", Count: intPtr(1)},
        {ID: "pvp_wins", Count: intPtr(25)},
    },
    Extra: map[string]interface{}{
        "guild_id":       "warriors_123",
        "premium_member": true,
    },
})

Parameters: See PixelsSnapshotParams for full parameter details.

Best Practice

Send player snapshots regularly (e.g., on login, after significant changes) to keep Stacked's user profiles up-to-date.


addTags

Add tags to users for segmentation and targeting.

analytics.addTags({
  player_ids: ['user-123', 'user-456'],
  tags: ['vip', 'early_adopter', 'high_spender']
});
err := analytics.AddTags(ctx, pixels.AddTagsParams{
    PlayerIDs: []string{"user-123", "user-456"},
    Tags:      []string{"vip", "early_adopter", "high_spender"},
})

Parameters: See PixelsAddTagsParams for full parameter details.


removeTags

Remove tags from users.

analytics.removeTags({
  player_ids: ['user-123'],
  tags: ['trial_user']
});
err := analytics.RemoveTags(ctx, pixels.RemoveTagsParams{
    PlayerIDs: []string{"user-123"},
    Tags:      []string{"trial_user"},
})

Parameters: See PixelsRemoveTagsParams for full parameter details.


Special Events

custom

Track any custom event specific to your application.

analytics.custom('guild_created', 'user-123', {
  guild_name: 'Warriors United',
  guild_size: 50,
  guild_level: 1
});
err := analytics.Custom(ctx, "guild_created", "user-123", pixels.CustomParams{
    "guild_name":  "Warriors United",
    "guild_size":  50,
    "guild_level": 1,
})

Parameters: See PixelsCustomParams for full parameter details.

Naming Convention

Custom event names must be in snake_case and cannot use reserved event names.


callContext

Trigger contextual offers by calling specific contexts.

analytics.callContext('user-123', {
  context: 'shop_open',
  extra: {
    shop_type: 'weapons',
    player_level: 15
  }
});
err := analytics.CallContext(ctx, "user-123", pixels.CallContextParams{
    Context: "shop_open",
    Extra: map[string]interface{}{
        "shop_type":    "weapons",
        "player_level": 15,
    },
})

Parameters: See PixelsCallContextParams for full parameter details.


setDynamicField

Set dynamic fields on user profiles for advanced targeting.

analytics.setDynamicField('user-123', {
  key: 'favorite_class',
  value: 'warrior'
});
err := analytics.SetDynamicField(ctx, "user-123", pixels.SetDynamicFieldParams{
    Key:   "favorite_class",
    Value: "warrior",
})

Parameters: See PixelsSetDynamicFieldParams for full parameter details.


incDynamicField

Increment a numeric dynamic field value.

analytics.incDynamicField('user-123', {
  key: 'login_count',
  value: 1
});
err := analytics.IncDynamicField(ctx, "user-123", pixels.SetDynamicFieldParams{
    Key:   "login_count",
    Value: 1,
})

Parameters: See PixelsSetDynamicFieldParams for full parameter details.


minDynamicField

Update a dynamic field only if the new value is less than the current value (or if the field doesn't exist).

analytics.minDynamicField('user-123', {
  key: 'best_time_seconds',
  value: 45.2
});
err := analytics.MinDynamicField(ctx, "user-123", pixels.SetDynamicFieldParams{
    Key:   "best_time_seconds",
    Value: 45.2,
})

Parameters: See PixelsSetDynamicFieldParams for full parameter details.


maxDynamicField

Update a dynamic field only if the new value is greater than the current value (or if the field doesn't exist).

analytics.maxDynamicField('user-123', {
  key: 'high_score',
  value: 15000
});
err := analytics.MaxDynamicField(ctx, "user-123", pixels.SetDynamicFieldParams{
    Key:   "high_score",
    Value: 15000,
})

Parameters: See PixelsSetDynamicFieldParams for full parameter details.


deleteDynamicField

Remove a dynamic field from a user profile.

analytics.deleteDynamicField('user-123', 'temporary_buff');
err := analytics.DeleteDynamicField(ctx, "user-123", "temporary_buff")

Trade Events

playerToPlayerTrade

Track trades between users.

analytics.playerToPlayerTrade({
  player1: {
    player_id: 'user-123',
    items: [{ id: 'sword_01', amount: 1 }],
    currencies: [{ id: 'gold', amount: 100 }]
  },
  player2: {
    player_id: 'user-456',
    items: [{ id: 'shield_01', amount: 1 }]
  }
});
err := analytics.PlayerToPlayerTrade(ctx, pixels.PlayerTradeParams{
    Player1: pixels.PlayerTradeData{
        PlayerID: "user-123",
        Items: []pixels.ItemData{
            {ID: "sword_01", Amount: 1},
        },
        Currencies: []pixels.CurrencyData{
            {ID: "gold", Amount: 100},
        },
    },
    Player2: pixels.PlayerTradeData{
        PlayerID: "user-456",
        Items: []pixels.ItemData{
            {ID: "shield_01", Amount: 1},
        },
    },
})

Parameters: See PixelsPlayerTradeParams for full parameter details.


Funnel Events

Track user progression through multi-step processes.

funnelStart

analytics.funnelStart('user-123', {
  funnel_id: 'onboarding',
  kind: 'start'
});
err := analytics.FunnelStart(ctx, "user-123", pixels.FunnelStartParams{
    FunnelID: "onboarding",
})

Parameters: See PixelsFunnelStartParams for full parameter details.


funnelProgression

analytics.funnelProgression('user-123', {
  funnel_id: 'onboarding',
  step_number: 2,
  step_id: 'character_creation'
});
err := analytics.FunnelProgression(ctx, "user-123", pixels.FunnelProgressionParams{
    FunnelID: "onboarding",
    StepID:   "character_creation",
})

Parameters: See PixelsFunnelProgressionParams for full parameter details.


funnelEnd

analytics.funnelEnd('user-123', {
  funnel_id: 'onboarding',
  kind: 'complete'
});
err := analytics.FunnelEnd(ctx, "user-123", pixels.FunnelEndParams{
    FunnelID: "onboarding",
})

Parameters: See PixelsFunnelEndParams for full parameter details.


User Linking Events

linkUser

Link another user or player to the current user. Useful for tracking relationships between players (e.g., referrals, guilds, friends).

analytics.linkUser('user-123', {
  user_id: 'friend-456',
  entity_kind: 'friend'
});
err := analytics.LinkUser(ctx, "user-123", pixels.LinkUserParams{
    UserID:     "friend-456",
    EntityKind: stringPtr("friend"),
})

Parameters: See PixelsLinkUserParams for full parameter details.


unlinkUser

Unlink a previously linked user or player from the current user.

analytics.unlinkUser('user-123', {
  user_id: 'friend-456'
});
err := analytics.UnlinkUser(ctx, "user-123", pixels.UnlinkUserParams{
    UserID: "friend-456",
})

Parameters: See PixelsUnlinkUserParams for full parameter details.


API Methods

These methods make API calls rather than tracking events.

getGameId

Returns the game ID associated with this SDK instance. Available after initialization.

const gameId = analytics.getGameId();
console.log('Game ID:', gameId);
gameId := analytics.GetGameID()
fmt.Println("Game ID:", gameId)

signJwt

Generate a JWT token for a player. This token can be used for client-side SDK authentication.

const token = await analytics.signJwt({ playerId: 'user-123' });
// Use token for client SDK authentication
token, err := analytics.SignJwt(ctx, "user-123")
if err != nil {
    log.Fatal(err)
}
// Use token for client SDK authentication

decodeJwt

Decode and validate a JWT token. Returns token details including validity, player ID, and game ID.

const result = await analytics.decodeJwt(token);
console.log('Valid:', result.valid);
console.log('Player ID:', result.playerId);
console.log('Game ID:', result.gameId);

// With custom secret
const result2 = await analytics.decodeJwt(token, 'custom-secret');
result, err := analytics.DecodeJwt(ctx, token)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Valid:", result.Valid)
fmt.Println("Player ID:", result.PlayerID)
fmt.Println("Game ID:", result.GameID)

// With custom secret
result2, err := analytics.DecodeJwt(ctx, token, "custom-secret")

fetchPlayerCampaigns

Fetch and refresh all currently active offers and referral programs for a player. Returns the player's offers, a JWT token, player data, and game ID.

const campaigns = await analytics.fetchPlayerCampaigns('user-123');
console.log('Offers:', campaigns.offers);
console.log('Token:', campaigns.token);
console.log('Player:', campaigns.player);

// With viewingCampaigns option (set to true if rendering in your own UI)
const campaigns2 = await analytics.fetchPlayerCampaigns('user-123', {
  viewingCampaigns: true
});
result, err := analytics.FetchPlayerCampaigns(ctx, "user-123")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Offers:", result.Offers)
fmt.Println("Token:", result.Token)
fmt.Println("Player:", result.Player)

// With viewingCampaigns option
viewingCampaigns := true
options := pixels.FetchPlayerCampaignsOptions{ViewingCampaigns: &viewingCampaigns}
result2, err := analytics.FetchPlayerCampaigns(ctx, "user-123", options)