TORQUEDocs

Building Landing Pages

Display leaderboards, offer info, and claim status for your incentive programs

How leaderboards, claims, epochs, and results connect, and how to build a landing page that displays incentive data and enables users to claim rewards.

Domain Model

Project
  └── Recurring Incentive (leaderboard | rebate | raffle | direct)
        └── Epoch (evaluation period)
              ├── Status lifecycle: UPCOMING → EVALUATING → CLAIMING → COMPLETED (or FAILED as terminal)
              ├── Query → Results (ranked wallets + metric values)
              └── Offer (on-chain, per-epoch reward distribution)

Key concepts:

  • A project has one or more recurring incentives.
  • Each incentive runs in epochs — time-bounded evaluation periods.
  • Epoch status lifecycle: UPCOMING → EVALUATING → CLAIMING → COMPLETED (or FAILED as terminal).
  • During EVALUATING, the query runs and produces results — ranked wallets with metric values (this is the leaderboard data).
  • During CLAIMING, eligible users can claim rewards via an API call with their wallet pubkey.
  • Each epoch has an associated offer that handles on-chain reward distribution. The offer is distinct from the top-level recurring incentive that owns the epochs.
  • Use get_recurring_incentive to inspect epoch details: status, eval/claim windows, offer IDs, query IDs.

Displaying Results & Scores

Via MCP Tools (authenticated, for builders)

ToolModeUse Case
get_epoch_leaderboardpreviewLeaderboard scores/rankings — ranked wallets and metric values from the query
get_epoch_leaderboardrecipientsAllocations and payouts — wallet addresses, amounts, and distribution status (DISTRIBUTED/PENDING/FAILED)
get_epoch_leaderboarddownloadFull CSV download/export URL (signed S3, expires 5 min) for the complete result set

Via REST API (public, for frontends)

Latest evaluation results (live scores):

GET /project/:projectId/recurring-offer/:id/latest-eval-results?limit=10&offset=0
  • Fully public, no auth required.
  • Returns ranked wallets with metric values from the most recent evaluation.
  • Use this for live leaderboard displays.

Recipients data (allocations and payout status) requires project authentication and should be fetched from your backend using the MCP get_epoch_leaderboard tool with mode: "recipients".

Epoch-Level Data (for dashboards & analytics)

Each recurring incentive runs in epochs — time-bounded evaluation periods. Each epoch carries its own configuration, which can change between epochs. Use get_recurring_incentive to access the configs array.

Distribution config (per epoch)

FieldDescription
distributionTypeFORMULA (leaderboard, rebate, direct) or RAFFLE (raffle). Legacy values DIRECT, MANUAL, CSV may appear on older projects but are not written by the MCP.
emissionTypeTOKENS or SOL. POINTS exists in the DB enum for non-MCP paths but create_recurring_incentive rejects it.
tokenAddressToken mint address (when emissionType is TOKENS)
tokenDecimalsToken decimal places (when emissionType is TOKENS)
totalFundAmountTotal reward budget for the epoch
customFormulaPayout formula (for FORMULA distribution)
prizeBucketsArray of {amount, count} prize tiers (when distributionType is RAFFLE)
selectionLogicWEIGHTED_BY_METRIC or EQUAL_CHANCES (when distributionType is RAFFLE)
maxPerParticipantPer-user reward cap (if set)
claimWindowDurationClaim period duration in seconds
distributionMethodAIRDROP or CLAIM

MCP input vs. persisted config names: create_recurring_incentive accepts raffleBuckets and raffleWeighting. Once persisted on the epoch config, they appear as prizeBuckets and selectionLogic. If you're reading analytics or epoch configs, expect the persisted names.

Offer metadata (per epoch)

FieldDescription
titleIncentive display title
descriptionOptional description
imageOptional image URL

Epoch timing

FieldDescription
evalStart / evalEndEvaluation window — when user activity is tracked
claimStart / claimEndClaim window — when users can claim rewards
statusUPCOMING, EVALUATING, CLAIMING, COMPLETED, or FAILED

Historical data across epochs

Distribution configs and metadata can change epoch-to-epoch (e.g., raising the reward pool, adjusting the formula). This means builders can:

  • Show reward history across epochs (how the incentive evolved).
  • Display per-epoch analytics (participants, total distributed, rewards per wallet).
  • Compare configs between epochs to highlight changes.
  • Build dashboards showing incentive performance over time.

Use get_epoch_leaderboard with a specific epochConfigId to fetch results or recipients for any historical epoch, not just the latest.

Displaying Offer Info

Via REST API (for frontends)

GET /claim/details/byOffer?projectId=X&offerStatus=ACTIVE
  • Works without auth for non-gated offers.
  • Add &wallet=<userPubkey> to also get:
    • isEligible — whether the wallet qualifies.
    • eligibleAmounts — reward amounts the wallet can claim.

Displaying Claim Status (Per User)

GET /claim/details/byAllocation?projectId=X&wallet=<userPubkey>
  • Works without auth for non-gated offers.
  • Returns per-allocation details:
FieldDescription
offerIdThe offer this allocation belongs to
amountReward amount
tokenReward token address
availableAtWhen the claim becomes available
crankNested object { id, status, signature } (or null if not yet queued). status follows the lifecycle below; signature is the Solana transaction signature, populated when status is DONE.

Crank status lifecycle (crank.status):

STAGED → STARTED → PENDING → DONE (or FAILED, or INVALID)
  • STAGED — Claim queued for processing.
  • STARTED — Processing has begun.
  • PENDING — Transaction submitted, waiting for confirmation.
  • DONE — Complete; crank.signature is the Solana transaction signature — link to Solana explorer.
  • FAILED — Processing failed; may be retried.
  • INVALID — Allocation is permanently ineligible and will not be retried.

Inspect the live API response for the current crank-status values rather than hard-coding them — the exact status set may evolve.

Poll this endpoint for real-time progress updates on the user's claims.

Enabling Claims

Claims are fully server-side — no transaction signing is required from the user's wallet.

1

Connect wallet

Use a wallet adapter (e.g. @solana/wallet-adapter-react) to get the user's pubkey.

2

Trigger claims

GET /claim?projectId=X&wallet=<userPubkey>

This queues all eligible claims for the wallet. Multiple claims may be available if the user qualifies for more than one allocation.

3

Poll for status

GET /claim/details/byAllocation?projectId=X&wallet=<userPubkey>

Watch crank.status progress from STAGED through DONE.

Auth Considerations

Endpoint CategoryAuth RequiredSafe For
Latest eval results (/project/:projectId/recurring-offer/:id/latest-eval-results)NoneClient-side, fully public
Claim trigger (/claim)Optional API keyClient-side for non-gated
Claim status (/claim/details/byAllocation)Optional API keyClient-side for non-gated
Offer details (/claim/details/byOffer)Optional API keyClient-side for non-gated
Incentive results (get_epoch_leaderboard)Project authBackend only
Recipients / payouts (get_epoch_leaderboard recipients mode)Project authBackend only
Epoch data (get_recurring_incentive)Project authBackend only
Query downloadsProject authBackend only

Rule of thumb: If it is user-facing display data (eval results, stats, claim status), it is public. If it is project management data (epoch configs, recipient allocations, raw query downloads), it requires project authentication and should only be called from your backend.

┌─────────────────────────────────────────────────┐
│                   FRONTEND                       │
│                                                  │
│  ┌──────────────┐  ┌─────────────────────────┐  │
│  │ Wallet       │  │ Public API Calls         │  │
│  │ Connect      │  │                          │  │
│  │ (get pubkey) │  │ • GET /project/:pid/     │  │
│  │              │  │     recurring-offer/:id/ │  │
│  │              │  │     latest-eval-results  │  │
│  └──────┬───────┘  │                          │  │
│         │          │                          │  │
│         │          │ • GET /claim              │  │
│         │          │ • GET /claim/details/...  │  │
│         │          │ • GET /claim/details/     │  │
│         │          │     byOffer               │  │
│         │          └─────────────────────────┘  │
│         │                                        │
│         └─── pubkey used for claim + status ──►  │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│                   BACKEND                        │
│                                                  │
│  ┌─────────────────────────────────────────────┐│
│  │ Authenticated Calls (project auth / API key) ││
│  │                                              ││
│  │ • get_recurring_incentive (epoch details)    ││
│  │ • get_epoch_leaderboard (eval results, CSV)  ││
│  │ • Gated offer details                        ││
│  └─────────────────────────────────────────────┘│
└─────────────────────────────────────────────────┘

Frontend handles wallet connection and all public endpoints — eval results display, offer info, claim triggering, and claim status polling. No secrets needed.

Backend handles authenticated calls for epoch management, raw evaluation results, and any API-key-gated offer queries. Keep project auth tokens and API keys server-side only.

Building Landing Pages - Torque Docs