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-hereExample 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)
| Error | Description | How to Handle |
|---|---|---|
jwt-expired | JWT has passed its expiration time | Generate a new JWT and retry |
jwt-invalid | JWT signature is invalid | Check your API credentials and JWT generation |
jwt-missing-gameId | JWT payload missing iss (gameId) claim | Ensure iss claim is set to your gameId |
jwt-missing-playerId | JWT payload missing sub (playerId) claim | Include sub in JWT payload |
jwt-not-yet-valid | JWT nbf (not before) time hasn't been reached | Check system clocks or wait |
unknown-gameId | gameId in JWT is not recognized | Verify your gameId is correct |
Example JWT Error:
{
"message": "jwt-expired"
}API Key Errors (Status 401)
| Error | Description | How to Handle |
|---|---|---|
invalid-api-key | API key or client ID is incorrect | Verify 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/generateAuthentication
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.
Stacked