Skip to main content

Leaderboard Service API Reference

The Leaderboard Service manages user statistics aggregation and leaderboard generation across all platform integrations. It provides rankings by various metrics (LTZ earned, activity score, streak, claims) and time periods (week, month, all-time).

Base URL

https://services.loyalteez.app

Endpoints

Get Leaderboard

Retrieves a ranked leaderboard for a specific metric and period.

Endpoint: GET /leaderboard/:brandId?metric=ltz_earned&period=all&platform=all&limit=10

URL Parameters:

  • brandId (path, required): The brand's wallet address
  • metric (query, optional): Metric to rank by. Default: "ltz_earned"
    • ltz_earned: Total LTZ tokens earned
    • activity: Activity score (calculated from LTZ, claims, streak)
    • streak: Current streak length
    • claims: Total number of claims
  • period (query, optional): Time period. Default: "all"
    • week: Weekly rankings
    • month: Monthly rankings
    • all: All-time rankings
  • platform (query, optional): Platform filter. Default: "all"
    • discord, telegram, twitter, farcaster, shopify, wordpress, all
  • limit (query, optional): Number of results. Default: 10

Response:

{
"success": true,
"leaderboard": [
{
"rank": 1,
"userIdentifier": "[email protected]",
"displayName": "TopUser",
"platform": "discord",
"value": 10000,
"formattedValue": "10,000 LTZ"
},
{
"rank": 2,
"userIdentifier": "[email protected]",
"displayName": "SecondPlace",
"platform": "telegram",
"value": 8500,
"formattedValue": "8,500 LTZ"
}
],
"metric": "ltz_earned",
"period": "all",
"platform": "all",
"updatedAt": "2024-01-15T10:30:00.000Z"
}

Response Fields:

  • success: Whether the operation succeeded
  • leaderboard: Array of ranked entries
    • rank: Position in leaderboard (1-indexed)
    • userIdentifier: User identifier
    • displayName: Display name for the user
    • platform: User's platform
    • value: Raw metric value
    • formattedValue: Human-readable formatted value
  • metric: Metric used for ranking
  • period: Time period used
  • platform: Platform filter used
  • updatedAt: Timestamp of last update

Example:

const brandId = encodeURIComponent('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');

const response = await fetch(
`https://services.loyalteez.app/leaderboard/${brandId}?metric=ltz_earned&period=week&platform=discord&limit=10`
);

const result = await response.json();

if (result.success) {
result.leaderboard.forEach(entry => {
console.log(`${entry.rank}. ${entry.displayName}: ${entry.formattedValue}`);
});
}

Update Stats

Updates user statistics after a reward is distributed. Call this after rewarding a user to keep leaderboard data current.

Endpoint: POST /leaderboard/update-stats

Request Body:

{
"brandId": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"userIdentifier": "[email protected]",
"platform": "discord",
"ltzAmount": 100,
"claimType": "daily_checkin",
"displayName": "Username"
}

Parameters:

  • brandId (required): The brand's wallet address
  • userIdentifier (required): User identifier
  • platform (required): Platform name
  • ltzAmount (required): LTZ amount earned (used for statistics)
  • claimType (optional): Type of claim/event
  • displayName (optional): User's display name

Response:

{
"success": true,
"stats": {
"brand_id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"user_identifier": "[email protected]",
"platform": "discord",
"display_name": "Username",
"ltz_earned_total": 10000,
"ltz_earned_week": 500,
"ltz_earned_month": 2000,
"activity_score": 2500,
"claims_count": 50,
"current_streak": 7,
"last_active_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:00.000Z"
},
"newRank": 5
}

Response Fields:

  • success: Whether the operation succeeded
  • stats: Updated user statistics
    • ltz_earned_total: Total LTZ earned (all-time)
    • ltz_earned_week: LTZ earned this week
    • ltz_earned_month: LTZ earned this month
    • activity_score: Calculated activity score
    • claims_count: Total number of claims
    • current_streak: Current streak length
  • newRank: User's new rank in all-time leaderboard (if in top 1000)

Example:

async function updateUserStats(userIdentifier, platform, brandId, ltzAmount) {
const response = await fetch('https://services.loyalteez.app/leaderboard/update-stats', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brandId,
userIdentifier,
platform,
ltzAmount,
claimType: 'daily_checkin',
displayName: 'Username'
})
});

const result = await response.json();

if (result.success) {
console.log(`Updated stats. New rank: ${result.newRank}`);
}
}

Metrics Explained

LTZ Earned (ltz_earned)

Total LTZ tokens earned by users. Available for all time periods (week, month, all).

Activity Score (activity)

Calculated score combining multiple factors:

  • LTZ earned: 1 point per 10 LTZ
  • Claims count: 5 points per claim
  • Streak: 10 points per streak day

Formula: (ltz_earned_total / 10) + (claims_count * 5) + (current_streak * 10)

Streak (streak)

Current streak length. Updated when streak service records activity.

Claims (claims)

Total number of reward claims/events. Incremented each time /leaderboard/update-stats is called.


Time Periods

Week (week)

Monday to Sunday (starts on Monday). Resets weekly for weekly rankings.

Month (month)

First day of month to last day. Resets monthly for monthly rankings.

All (all)

All-time statistics since user's first activity. Never resets.


Activity Score Calculation

Activity scores are automatically calculated when stats are updated:

function calculateActivityScore(stats) {
const ltzScore = (stats.ltz_earned_total || 0) / 10; // 1 point per 10 LTZ
const claimsScore = (stats.claims_count || 0) * 5; // 5 points per claim
const streakScore = (stats.current_streak || 0) * 10; // 10 points per streak day

return Math.floor(ltzScore + claimsScore + streakScore);
}

This provides a balanced ranking that rewards:

  • High earners (LTZ)
  • Active participants (claims)
  • Consistent users (streaks)

Integration Examples

Discord Bot - Leaderboard Command

async function handleLeaderboard(brandId, metric = 'ltz_earned', period = 'all') {
const response = await fetch(
`https://services.loyalteez.app/leaderboard/${encodeURIComponent(brandId)}?metric=${metric}&period=${period}&limit=10`
);

const result = await response.json();

if (!result.success) {
return 'Error fetching leaderboard';
}

let message = `🏆 Leaderboard (${metric}, ${period}):\n\n`;

result.leaderboard.forEach(entry => {
const medal = entry.rank === 1 ? '🥇' : entry.rank === 2 ? '🥈' : entry.rank === 3 ? '🥉' : `${entry.rank}.`;
message += `${medal} ${entry.displayName}: ${entry.formattedValue}\n`;
});

return message;
}

Update Stats After Reward

async function rewardUser(userIdentifier, platform, brandId, ltzAmount) {
// First, reward the user (via Event Handler API)
await fetch('https://api.loyalteez.app/loyalteez-api/manual-event', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brandId,
eventType: 'daily_checkin',
userEmail: userIdentifier,
metadata: { platform }
})
});

// Then, update leaderboard stats
await fetch('https://services.loyalteez.app/leaderboard/update-stats', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brandId,
userIdentifier,
platform,
ltzAmount,
claimType: 'daily_checkin'
})
});
}

Web Application - Weekly Leaderboard

async function getWeeklyLeaderboard(brandId) {
const response = await fetch(
`https://services.loyalteez.app/leaderboard/${encodeURIComponent(brandId)}?metric=activity&period=week&limit=20`
);

const result = await response.json();

if (result.success) {
displayLeaderboard(result.leaderboard);
}
}

Best Practices

  1. Update Stats After Rewards: Always call /leaderboard/update-stats after distributing rewards
  2. Use Appropriate Metrics: Choose the right metric for your use case:
    • ltz_earned: Best for showing top earners
    • activity: Best for overall engagement
    • streak: Best for consistency
    • claims: Best for participation
  3. Filter by Platform: Use platform filters to show platform-specific leaderboards
  4. Limit Results: Use appropriate limits (10-50) to avoid overwhelming users
  5. Cache Responses: Cache leaderboard data and refresh periodically (e.g., every 5 minutes)
  6. Handle Errors: Always handle errors gracefully and show user-friendly messages

Performance Considerations

  • Leaderboard queries are optimized for performance
  • Results are sorted by database indexes
  • Consider caching responses for frequently accessed leaderboards
  • Weekly/monthly periods reset automatically at period boundaries
  • Limit results to reasonable numbers (10-50) for best performance