Skip to main content
Quests keep players engaged throughout your game’s lifecycle. Players complete objectives, earn rewards, and climb leaderboards—whether your game is pre-launch or live.

Overview

The Audience Builder Program includes in-game quests hosted on your game’s Audience Builder page in Immutable Play. Players sign up using Immutable Passport (simple social sign-on) to:
  • Access the Immutable Play platform, game quests, and leaderboard competitions
  • Track questing and leaderboard progress
  • Receive rewards
  • Link social accounts to their Immutable Play account
No Passport integration required in-game. Your game doesn’t need to implement Immutable Passport as its login/wallet system or integrate any Immutable chain infrastructure. Simply track player progress using your existing backend and notify Immutable when a quest is completed via a lightweight API call.

Responsibilities

TaskOwnerDescription
Campaign & quest designStudio + ImmutableCollaborate to define quest structure, reward types, and timing
Player onboarding & linkingImmutablePlayers link an identifier matched to your backend during onboarding
Game login & player trackingStudioUse your existing login and backend systems
Quest completion submissionStudioTrack quest progress in-game and send completions to Immutable’s Quest API
Reward matching & deliveryImmutableMatch identifier to Passport and deliver rewards

How to Integrate

1

Define Quests

Work with Immutable to agree on quests (e.g., “Play 30 arena matches”) and their rewards
2

Get API Access

Create an API key on Immutable Hub and share your Org ID
3

Track Quest Logic

Use your existing backend to monitor when players meet quest criteria
4

Submit Quest Completion

When a quest is completed, send a POST request to the Immutable Quest API with the player’s linked identifier
5

Rewards Delivered

Immutable handles player matching and instantly updates their Play inventory

Supported Identifiers

Players can link one of these accounts to their Passport. Use this identifier when submitting quest completions.
Account TypeAccount IDExample
telegramTelegram user ID1191097385
discordDiscord user ID1374611107196440770
emailEmail addressuser@example.com
metamaskMetaMask wallet address0xc257274276a4e539741ca11b590b9447b26a8051
passportPassport wallet address0xa9361c0dd68e1c3a69b43f646133fdab8d3859a2
epic_gamesEpic Games user IDa26eca91eca340a7a8cadb886e7c1190
Notes on account types:
  • email is not supported in Sandbox. Use another account type for testing.
  • epic_games account linking is not yet supported in Immutable Play. However, quest completions with epic_games will be stored and credited once linking is available—no progress will be lost.

Quest Types

In-Game Quests

Track gameplay actions: matches played, levels completed, items collected

Social Quests

Follow on Twitter, join Discord, share content, engage with posts

Referral Quests

Invite friends, reach referral milestones, build your network

On-Chain Quests

Verify blockchain actions: first trade, first mint, collection completion

Leaderboards

Gamify your community with competitive rankings:
LeaderboardCriteriaRewards
All-TimeTotal points earnedFounder badges, exclusive items
WeeklyPoints this weekFeatured spotlight, bonus rewards
ReferralFriends referredVIP access, revenue share
Players can spend gems to showcase their username on leaderboards across Immutable Play.

Questing API

The Questing API provides endpoints for recording quest completions, retrieving active quests, and fetching player earnings. Use these endpoints to integrate quest functionality into your game backend.

Prerequisites

Required Quest: Login and Play Game

As part of the Quest API integration, partners are required to implement the Login and Play Game quest, identified by the login-play-game external_id. This quest is configured as a daily recurring quest and should be triggered every time a player logs in. While triggered on login, it tracks participation on a daily basis, providing valuable insights into player engagement and recurring behavior patterns.
{
  "external_id": "login-play-game",
  "account_type": "discord",
  "account_id": "901290221440733204"
}

Important Notes

Quest completions are stored for non-Passport users. If the identified user is not yet a registered Immutable Passport user, the quest completion will be stored with a reference to the account type and ID supplied in the request. When the user registers with Immutable Passport and links that account, they will be instantly rewarded and credited for this quest (and any other stored completions). This means user accounts do not need to be current Passport users when making requests.
Always use the reference field for idempotency. We highly recommend using a unique reference field (e.g., {accountId}-{externalId}-{timestamp}) to ensure the same user cannot complete the same quest twice. If you receive a 409 Conflict response, the quest completion was already recorded—don’t retry.

API Endpoints

The Questing API provides three main endpoints:
  • Quest Completions — Record quest completions and trigger rewards (requires authentication)
  • Get Live Quests — Retrieve all currently active quests for your game (no authentication required)
  • Latest Earnings — Fetch player gem earning history by wallet address (no authentication required)
Complete API documentation for all endpoints, including request/response schemas, authentication details, and code examples, is available in the API Reference tab.

Best Practices

Follow these best practices to ensure reliable quest completion tracking and optimal integration with the Quest API.

Idempotency

Always use the reference field to prevent duplicate quest completions. Recommended Pattern Use a unique reference string that combines player identifier, quest ID, and timestamp:
const reference = `${accountId}-${externalId}-${Date.now()}`;
Why It Matters
  • Prevents accidental duplicate submissions
  • Enables safe retries on network failures
  • Provides audit trail for debugging
If you receive a 409 Conflict response, the quest completion was already recorded. Don’t retry the request—treat it as success.

Error Handling

Implement robust error handling with appropriate retry logic. Retry Strategy
async function submitQuestCompletionWithRetry(
  gameId: string,
  apiKey: string,
  externalId: string,
  accountType: string,
  accountId: string,
  reference: string,
  maxRetries = 3
) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(
        `https://api.immutable.com/quests/v2/games/${gameId}/quest-completions`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'x-immutable-api-key': apiKey,
          },
          body: JSON.stringify({
            external_id: externalId,
            account_type: accountType,
            account_id: accountId,
            reference,
            completion_time: new Date().toISOString(),
          }),
        }
      );

      // Success - return immediately
      if (response.ok) {
        return await response.json();
      }

      // 409 Conflict - already recorded, treat as success
      if (response.status === 409) {
        console.log(`Quest completion already recorded: ${reference}`);
        return { result: { status: 'already_completed' } };
      }

      // 4xx errors - don't retry (client error)
      if (response.status >= 400 && response.status < 500) {
        const error = await response.json();
        throw new Error(`Client error: ${error.message || response.statusText}`);
      }

      // 5xx errors - retry with exponential backoff
      if (response.status >= 500) {
        if (attempt < maxRetries) {
          const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
        throw new Error(`Server error after ${maxRetries} attempts`);
      }
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
      // Network errors - retry with exponential backoff
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
Error Response Handling
Status CodeAction
200Success - quest completion recorded
409Already recorded - treat as success, don’t retry
400, 401, 403, 404Client error - don’t retry, log and investigate
500, 504Server error - retry with exponential backoff

Logging

Always log quest completion requests for debugging and auditing. What to Log
function logQuestCompletion(
  externalId: string,
  accountType: string,
  accountId: string,
  reference: string,
  status: 'success' | 'error',
  error?: string
) {
  const logEntry = {
    timestamp: new Date().toISOString(),
    quest: externalId,
    account: { type: accountType, id: accountId },
    reference,
    status,
    error,
  };

  // Log to your preferred logging service
  console.log(JSON.stringify(logEntry));
  
  // Optionally send to monitoring service
  // sendToMonitoring(logEntry);
}
Logging Best Practices
  • Log before making the API request
  • Log the response status and any errors
  • Include the reference ID for correlation
  • Don’t log sensitive data (mask account IDs if needed)
  • Use structured logging for easier querying

Rate Limiting

The Quest API implements rate limiting to ensure fair usage. Rate Limit Tiers
TierRate Limit
Standard50 req/sec
EnterpriseCustom
Default rate limits:
  • Global default: 5 requests per second per public IP
  • Partner adjusted rate limit: 50 requests per second per API key
  • Enterprise: Custom rate limits based on your agreement
Recommendations
  • Batch quest completions when possible (if multiple quests complete simultaneously)
  • Implement client-side rate limiting to avoid hitting limits
  • Use exponential backoff when receiving rate limit errors (typically 429 status)
  • Monitor your API usage in Immutable Hub — see Hub Overview for details
Handling Rate Limits
if (response.status === 429) {
  const retryAfter = response.headers.get('Retry-After');
  const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000; // Default 60s
  await new Promise(resolve => setTimeout(resolve, delay));
  // Retry the request
}

Account ID Validation

Validate account IDs before submitting to ensure correct format. Validation Examples
function validateAccountId(accountType: string, accountId: string): boolean {
  switch (accountType) {
    case 'discord':
      // Discord IDs are numeric strings (snowflakes)
      return /^\d+$/.test(accountId);
    
    case 'telegram':
      // Telegram IDs are numeric
      return /^\d+$/.test(accountId);
    
    case 'metamask':
    case 'passport':
      // Wallet addresses are hex strings starting with 0x
      return /^0x[a-fA-F0-9]{40}$/.test(accountId);
    
    case 'email':
      // Basic email validation
      return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(accountId);
    
    case 'epic_games':
      // Epic Games IDs are alphanumeric strings
      return /^[a-zA-Z0-9]+$/.test(accountId);
    
    default:
      return false;
  }
}

Async Processing

Consider processing quest completions asynchronously to avoid blocking game logic. Queue-Based Approach
// Add to queue instead of blocking
questCompletionQueue.add({
  externalId: 'catch-3-fish',
  accountType: 'discord',
  accountId: playerDiscordId,
  reference: generateReference(),
});

// Process queue in background
async function processQuestQueue() {
  while (true) {
    const item = await questCompletionQueue.get();
    try {
      await submitQuestCompletion(item);
    } catch (error) {
      // Log error and potentially re-queue
      logError(error);
    }
  }
}

Testing

Test your integration thoroughly before going live. Test Scenarios
  1. Successful completion - Verify quest is recorded correctly
  2. Duplicate submission - Ensure idempotency works with same reference
  3. Invalid account ID - Verify proper error handling
  4. Network failures - Test retry logic
  5. Rate limiting - Verify backoff behavior
  6. Required quest - Ensure login-play-game is triggered on login
Sandbox Testing
  • Use Sandbox environment for all testing
  • Test with discord, telegram, or metamask account types (not email)
  • Verify quest completions appear in Immutable Play (Sandbox)

Monitoring

Set up monitoring and alerts for quest completion failures. Key Metrics to Track
  • Quest completion success rate
  • API error rates by status code
  • Average response time
  • Rate limit hits
  • Duplicate completion attempts
Alert Thresholds
  • Error rate > 5%
  • Response time > 2 seconds
  • Rate limit errors detected

FAQ

No. The Quest API is designed to work with your existing authentication system. Players link their accounts (Discord, Telegram, email, etc.) to Immutable Passport through Immutable Play, but your game doesn’t need to implement Passport authentication.
Quest completions are stored retroactively. When a player registers with Immutable Passport and links their account (Discord, Telegram, etc.), they’ll automatically receive credit for all previously completed quests. Quest completions are stored with the account type and ID you provide—when the player eventually links that account to Passport, they’ll receive all stored rewards.
Yes. Quest completions are stored with the account type and ID you provide. When the player eventually links that account to Immutable Passport, they’ll receive all stored rewards.
The login-play-game quest is a required daily recurring quest that tracks player engagement. You must trigger this quest every time a player logs into your game. It provides valuable data on daily active users and recurring behavior patterns.
Always include a unique reference field in your quest completion requests. Use a pattern like {accountId}-{externalId}-{timestamp}. If you receive a 409 Conflict response, the quest was already recorded—don’t retry.
Supported account types are: telegram, discord, email, metamask, passport, and epic_games. Note that email is not supported in Sandbox—use discord, telegram, or metamask for testing.Important: metamask and other third-party wallets need to be linked by the user either through the Play profile settings or a custom UX built by the studio using the link wallet API/SDK functionality.
Your Game ID is provided by Immutable’s Growth team during onboarding. Contact them if you don’t have it yet.
A 409 Conflict means the quest completion was already recorded (likely due to a duplicate reference). Treat this as success—don’t retry the request.
  • 4xx errors (400, 401, 403): Don’t retry—these indicate client errors that need investigation
  • 404 Not Found: Don’t retry—see common causes below
  • 409 Conflict: Treat as success—quest already recorded
  • 429 Rate Limit: Wait for the Retry-After header duration, then retry
  • 5xx errors (500, 504): Retry with exponential backoff
A 404 Not Found error can occur when:
  • The quest doesn’t exist (check the external_id is correct)
  • The quest has expired (check valid_from and valid_to dates)
  • The request was made to a production endpoint with a sandbox quest ID (or vice versa)
Verify you’re using the correct environment (Sandbox vs Production) and that the quest is active. If the issue persists, contact your Growth team.
The Quest API accepts one quest completion per request. If multiple quests complete simultaneously, submit them sequentially. Consider using a queue system to handle high-volume scenarios.
Use the Sandbox environment (https://api.sandbox.immutable.com) for testing. Test with discord, telegram, or metamask account types. Verify quest completions appear in Immutable Play (Sandbox).Additionally, studios can:
  • Visit play.sandbox.immutable.com to see quest and reward status
  • Use the Live Quests endpoint (GET /v1/quests/games/{game_id}/quests) to see the number of quest completions
The completion_time field allows you to specify when the quest was actually completed (in RFC3339 UTC format). This is useful for retroactive attribution or batch processing. If omitted, the API uses the current time.
Rewards are typically delivered instantly when a quest completion is successfully recorded. If a player hasn’t linked their account yet, rewards are stored and delivered when they link their account to Passport.
Quest completion data is visible in Immutable Play. Players can see their quest progress and rewards in their Play inventory. For analytics, you can view quest completion data via the Immutable Hub Insights feature. The Insights feature is enabled with an Immutable Audience Builder subscription. You can also contact Immutable’s Growth team for additional analytics.
Quest definitions are managed in collaboration with Immutable’s Growth team. Contact them to discuss changes to quest structure, rewards, or timing.
Yes, the Quest API implements rate limiting to ensure fair usage:
  • Standard tier: 50 requests per second per API key
  • Global default: 5 requests per second per public IP
  • Enterprise: Custom rate limits based on your agreement
If you receive a 429 Too Many Requests response, implement exponential backoff and retry after the duration specified in the Retry-After header.Common causes of 429 errors:
  • The API key is not being used correctly in the request flow
  • Sub-optimal integration where too many requests are made than needed
  • Increase in base traffic (which is a great sign for the project)
If you experience persistent 429 errors, contact your Growth team to review your integration and discuss potential optimizations or rate limit adjustments.
Yes, if your API key has the necessary permissions. However, ensure your key has access to the Quest API endpoints for your game.
Contact Immutable support through support.immutable.com or reach out to your Growth team contact for Quest API-specific questions.

Get API Access

Create your API key in Immutable Hub

Contact Us

Get help setting up your quest campaign

Next Steps

Audience Overview

Learn about the full Audience suite

Game Page

Set up your pre-launch landing page

Engagement

Configure quest completion campaigns

Attribution

Track quest performance data