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 authenticationtoken, err := analytics.SignJwt(ctx, "user-123")
if err != nil {
log.Fatal(err)
}
// Use token for client SDK authenticationdecodeJwt
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)
Stacked