TANITARDevelopers
GuidesGamification

Seasons and chests

Author battle-pass seasons with free and premium tiers, and design the chests players earn and open: drop tables, pity, currency and direct grants.

Battle-pass seasons

A season is a time-boxed ladder of tiers. Players earn battle-pass XP while the season is live; each tier has an XP threshold and up to two rewards, one on the free track and one on the premium track. Players claim the tiers they reach from the hosted experience.

RouteScopeWhat it does
GET /v1/gamification/battle-pass/seasonsgamification-catalog:authorList your seasons, newest seasonNumber first (no pagination)
POST /v1/gamification/battle-pass/seasonsgamification-catalog:authorCreate a season, or replace the one with the same seasonKey
PATCH /v1/gamification/battle-pass/seasons/:seasonKeygamification-catalog:authorReplace an existing season; send the full definition. An unknown key is 404 BATTLE_PASS_SEASON_NOT_FOUND.
POST /v1/gamification/battle-pass/seasons/:seasonKey/activategamification-catalog:authorEnable this season and disable every other one
curl -X POST "$GAMIFICATION_API_URL/v1/gamification/battle-pass/seasons" \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "seasonKey": "season-4",
    "name": "Season 4",
    "seasonNumber": 4,
    "startsAt": "2026-10-01T00:00:00Z",
    "endsAt": "2026-12-31T23:59:59Z",
    "tiers": [
      { "tier": 1, "xpRequired": 0, "freeReward": { "rewardType": "xp", "amount": 50 } },
      { "tier": 2, "xpRequired": 300,
        "freeReward": { "rewardType": "chest", "key": "common" },
        "premiumReward": { "rewardType": "currency", "amount": 100, "key": "gems" } },
      { "tier": 3, "xpRequired": 800, "premiumReward": { "rewardType": "badge", "key": "season-4-elite" } }
    ],
    "eventExperience": [{ "eventKey": "match:won", "amount": 20 }]
  }'
FieldRules
seasonKeyLowercase letters, digits and hyphens
seasonNumberA positive integer. If more than one enabled season covers the current time, the highest number is live.
startsAt, endsAtISO 8601; endsAt must be after startsAt. The season is live only between them.
tiers[]At least one. Numbered 1 to n with no gaps, xpRequired never decreasing from one tier to the next.
freeReward, premiumRewardOptional, or null. rewardType is xp (needs amount), currency (needs amount and a published currency key), chest (tier in key) or badge (badge key).
eventExperience[]Optional. Flat battle-pass XP awarded each time a player sends this event. Each event must be published or built in, and listed once.

A new season is stored disabled. Enabling it with activate disables every other season, so you always have at most one enabled season.

A rule breach is a 422 (GAMIFICATION_CADENCE_INVALID for ordering and window, GAMIFICATION_FIELD_INVALID for a reward, GAMIFICATION_EVENT_NOT_ADMISSIBLE for an unpublished event, GAMIFICATION_REGISTRY_REFERENCE_INVALID for an unknown currency).

An enabled season is locked

Once a season is enabled, players hold tier progress and claims against its tiers, so the terms cannot change under them. While a season is enabled, a write to it may only:

  • change name and description, and
  • move endsAt later.

Any change to seasonNumber, startsAt, tiers or eventExperience, or an earlier endsAt, is refused with 409 BATTLE_PASS_SEASON_ACTIVE_IMMUTABLE, naming the field. The refusal is decided before any registry is read. To change a live season's terms, create its successor and enable that one.

How players earn battle-pass XP

All battle-pass XP goes through one funnel toward the live season:

  • a mission reward of type battlePassXP;
  • a battle-pass XP drop from a chest;
  • the season's eventExperience amount for an event you send, once per event.

A player's tier only moves up. Each award publishes gamification.battle-pass.progressed with the amount and the player's tier. With no live season, battle-pass XP is not stored: the mission reward slot is recorded as rejected and the mission still completes.

Tier claims

Players claim a reached tier on one track from the hosted experience; there is no API-key route to claim for them. A claim is refused if there is no live season, the tier does not exist or has no reward on that track, the player has not reached it, it is already claimed, or the premium track is claimed by a player whose membership tier does not unlock it (BATTLE_PASS_PREMIUM_REQUIRED). Which tiers unlock the premium track is your membership-tier setting.

What happens to the reward depends on its type:

RewardSettles
xp, chestImmediately, in the claim itself
currencyThrough a Rewards grant. The claim stays pending until the grant settles. If Rewards rejects the grant, the tier is released and the player can claim it again. If no answer arrives before the grant timeout, the tier stays held, because the credit might still land.
badgeThrough a Rewards award

Each claim publishes gamification.battle-pass.tier-claimed. See Grants.

Chests

A chest is an item in a player's inventory with a common, rare, epic or legendary tier. It can also use one of your own chest types. Players get chests from:

  • a mission reward of type chest;
  • a battle-pass tier reward of type chest;
  • streak milestones (3, 7, 14 and 30 days);
  • a direct grant from your integration (below).

Each source creates the chest once, however often it is retried. Players open their chests from the hosted experience. Opening is all-or-nothing: the chest is marked opened, the drops are rolled and paid, and history is written in one transaction, or the chest stays unopened. Opening a chest also counts as the built-in event gamification:chest-opened, which missions can react to, and publishes gamification.chest.opened.

Drops and pity

Each open rolls the chest's guaranteed drops plus a chance of a bonus drop. Each drop rolls a rarity, then a reward type (currency, xp, battlePassXP or badge), then a value from the chest tier's range scaled by the rarity multiplier.

Chest tierGuaranteed dropsBonus drop chance
common15%
rare110%
epic215%
legendary325%

Pity is a bad-luck floor. Tanitar counts each player's opens since their last epic and last legendary drop. When an open reaches its threshold, 10 opens for epic and 40 for legendary by default, that open's first drop is raised to at least that rarity. A drop of that rarity or better resets the counter. Players can see their pity meter in the hosted experience.

XP and battle-pass XP drops are paid inside the open. A currency drop is sent to Rewards as a grant; a badge drop as an award.

Chest economy

The chest economy is your version of the drop tables, pity thresholds, reward pools and custom chest types. Until you author one, the default economy applies (revision 0). Like the level curve, each change appends a revision, and every opening records the revision it was rolled under.

RouteScopeWhat it does
GET /v1/gamification/chests/economygamification-catalog:authorThe economy in effect
GET /v1/gamification/chests/economy/:revisiongamification-catalog:authorA past revision; 0 is the default
PUT /v1/gamification/chests/economygamification-catalog:authorReplace the economy
curl -X PUT "$GAMIFICATION_API_URL/v1/gamification/chests/economy" \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "If-Match: \"1\"" \
  -H "Content-Type: application/json" \
  -d '{
    "pity": { "epicThreshold": 8, "legendaryThreshold": 30 },
    "pools": {
      "currency": [{ "key": "gems", "weight": 3 }, { "key": "coins", "weight": 1 }],
      "badge": [{ "key": "lucky-opener", "weight": 1 }]
    },
    "definitions": [
      { "chestKey": "halloween", "displayName": "Halloween chest", "baseTier": "rare",
        "tables": { "dropCounts": { "rare": { "guaranteedDrops": 2, "bonusDropChance": 0.2 } } } }
    ]
  }'
FieldWhat it sets
pityepicThreshold and legendaryThreshold, each 1 to 10,000; the legendary threshold must be greater than the epic one
tablesOverrides of rarityWeights, rewardTypeWeights, baseValues, rarityMultipliers and dropCounts, keyed by chest tier or rarity
poolsWeighted currency and badge keys (up to 50 each) a drop picks from. Every key must be published in Rewards.
definitions[]Up to 50 custom chest types, each with a chestKey, a displayName (60 characters at most), a baseTier, and its own optional tables and pools

Every rule is checked before any registry is read, and a breach is one 422 GAMIFICATION_CHEST_CONFIG_INVALID naming the field: an inverted range, a tier left with nothing it can roll, an unknown key. Once you have stored an economy, If-Match must carry its current revision (400 GAMIFICATION_PRECONDITION_REQUIRED when missing, 409 GAMIFICATION_CHEST_ECONOMY_REVISION_MISMATCH when stale).

Chest currency

RouteScopeWhat it does
GET /v1/gamification/chests/settingsgamification-catalog:authorRead the chest currency
PUT /v1/gamification/chests/settingsgamification-catalog:authorSet it: { "currencyKey": "gems" }, or null to clear

The chest currency is the currency a currency drop pays when no currency pool picks one. It must be a currency you have published. With no chest currency and no currency pool, chests drop no currency at all. Clearing it is refused if that would leave a tier of your economy with nothing to roll.

Grant a chest

Your integration can hand a chest to a player directly, for example as a support gesture or a prize.

RouteScopeIdempotency
POST /v1/gamification/chests/grantsgamification-progress:replayIdempotency-Key required
curl -X POST "$GAMIFICATION_API_URL/v1/gamification/chests/grants" -d '{ "playerId": "player-2231", "chestType": "rare", "chestKey": "halloween" }' \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json"
{ "inventoryId": "…", "created": true, "chestType": "rare", "chestKey": "halloween" }
  • chestType is the tier the chest opens as. chestKey is optional and must name a custom chest type in your stored economy (422 GAMIFICATION_CHEST_CONFIG_INVALID otherwise).
  • The Idempotency-Key is the grant's identity: resending the same key never creates a second chest. It answers with the same inventoryId (and "created": false once the request-level idempotency record has expired).
  • The grant records which principal made it.

On this page