LogoStacked
API

Authentication

JWT and API Key authentication for the Stacked API

Stacked supports two authentication methods designed for different environments and security requirements.

Security Best Practice

Never expose your API key or client ID in client-side code. Always generate JWTs on your server and send them to clients. API keys should only be used in server-to-server communication.

JWT Authentication (Client-Side)

Used for client-side requests from end-user devices (web browsers, mobile apps, game clients).

How It Works

Request Headers

Content-Type: application/json
x-game-jwt: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Or using standard Bearer token:

Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

JWT Requirements

The JWT must be signed using the HS256 algorithm with your Stacked API key as the secret.

Required Claims:

{
  "iss": "your-game-id",      // Issuer - MUST be your gameId
  "sub": "player-123",        // Subject - The player's unique ID
  "exp": 1735689600,          // Expiration time (Unix timestamp in seconds)
  "nbf": 1735603200           // Not before time (Unix timestamp in seconds, optional)
}

Example API Request

curl -X POST https://api.pixels.xyz/v1/client/player/campaigns \
  -H "Content-Type: application/json" \
  -H "x-game-jwt: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -d '{"viewingCampaigns": true}'

API Key Authentication (Server-Side)

Used for server-to-server requests from your game servers.

How It Works

Request Headers

Content-Type: application/json
x-api-key: your-api-key-here
x-client-id: your-client-id-here

Example Request

curl -X POST https://api.pixels.xyz/v1/player/campaigns/refresh \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key-here" \
  -H "x-client-id: your-client-id-here" \
  -d '{
    "playerId": "player-123",
    "viewingCampaigns": false
  }'

Error Responses

JWT Errors (Status 400/401)

ErrorDescriptionHow to Handle
jwt-expiredJWT has passed its expiration timeGenerate a new JWT and retry
jwt-invalidJWT signature is invalidCheck your API credentials and JWT generation
jwt-missing-gameIdJWT payload missing iss (gameId) claimEnsure iss claim is set to your gameId
jwt-missing-playerIdJWT payload missing sub (playerId) claimInclude sub in JWT payload
jwt-not-yet-validJWT nbf (not before) time hasn't been reachedCheck system clocks or wait
unknown-gameIdgameId in JWT is not recognizedVerify your gameId is correct

Example JWT Error:

{
  "message": "jwt-expired"
}

API Key Errors (Status 401)

ErrorDescriptionHow to Handle
invalid-api-keyAPI key or client ID is incorrectVerify your credentials

Example API Key Error:

"invalid-api-key"

JWT Management

Best Practices

JWT Lifespan:

  • JWTs should expire after ~15 minutes for security
  • Cache JWTs client-side while they're valid to reduce server requests
  • Only request a new JWT when the current one expires or is about to expire

Automatic Management:

For automatic JWT caching, refresh, and token management, use our official client SDKs:

These SDKs handle token expiration, caching, and refresh automatically.


Creating a JWT Endpoint

Your server needs an endpoint to generate JWTs for your clients. We recommend using the server-side SDK to handle this:

Using the Server-Side SDK (Recommended):

The Node.js SDK provides a signJWT() method that handles JWT generation for you:

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

const stacked = new PixelsAnalytics({
  apiKey: process.env.STACKED_API_KEY,
  clientId: process.env.STACKED_CLIENT_ID,
  env: 'live'
});

app.post('/api/stacked/jwt', authenticateUser, async (req, res) => {
  const jwt = await stacked.signJWT({
    playerId: req.user.playerId
  });

  res.json({ jwt });
});

Manual Implementation:

If you prefer not to use the SDK, you can manually generate JWTs:

const jwt = require('jsonwebtoken');

app.post('/api/stacked/jwt', authenticateUser, (req, res) => {
  const token = jwt.sign(
    {
      iss: 'your-game-id',
      sub: req.user.playerId,
      exp: Math.floor(Date.now() / 1000) + (15 * 60), // 15 minutes
      nbf: Math.floor(Date.now() / 1000)
    },
    process.env.STACKED_API_KEY,
    { algorithm: 'HS256' }
  );

  res.json({ jwt: token });
});

Generate One-Time Token

Generate a one-time use token to redirect a player from your game to the Stacked dashboard.

Endpoint

POST /auth/one_time_token/generate

Authentication

JWT - Requires x-game-jwt header

Request

curl -X POST https://api.pixels.xyz/v1/auth/one_time_token/generate \
  -H "Content-Type: application/json" \
  -H "x-game-jwt: YOUR_JWT_TOKEN" \
  -d '{}'

Response

{
  token: string; // One-time token, expires in 5 minutes
}

Example:

{
  "token": "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz..."
}

Use Case

Redirect a player from your game client to the Stacked rewards dashboard:

// 1. Generate one-time token
const response = await fetch('https://api.pixels.xyz/v1/auth/one_time_token/generate', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-game-jwt': jwt
  },
  body: JSON.stringify({})
});

const { token } = await response.json();

// 2. Redirect to Stacked dashboard
window.location.href = `https://offers.pixels.xyz/rewards?token=${token}&gameId=${gameId}`;

Token Expiry

One-time tokens expire after 5 minutes and can only be used once. Generate a fresh token each time you redirect a player.