> For the complete documentation index, see [llms.txt](https://series-1.gitbook.io/rundot-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://series-1.gitbook.io/rundot-docs/v5.21.0/readme/collectibles.md).

# Collectibles API (BETA)

Read the card catalog deployed with your game and let players claim subscriber-exclusive VIP cards. Card ownership itself flows through the [Entitlements API](/rundot-docs/v5.21.0/readme/entitlements.md) (each card is a non-consumable entitlement); the methods on this namespace are for catalog hydration and VIP-tier claims.

***

## Overview

A *card* is one non-consumable entitlement plus a chunk of presentation metadata (title, rarity, art paths, etc.). The metadata lives in your project's `.rundot/collectibles.config.json` and is deployed with `rundot deploy`. The Collectibles API gives end users a way to:

1. **Fetch the deployed catalog** on game init so they can render owned-vs-unowned cards.
2. **Claim VIP-tier cards** for which they are eligible (active subscription + completed series).

How players *earn* free / completion cards (watch threshold crossings, rule eval) is described in the [Stats API](/rundot-docs/v5.21.0/readme/stats.md). How players *purchase* premium cards is described in the [Shop API](/rundot-docs/v5.21.0/readme/shop.md). All three paths produce the same kind of artifact — an entitlement the platform records.

***

## API

The Collectibles API lives on `RundotGameAPI.collectibles`. Two methods.

### `listCards()`

```ts
const catalog = await RundotGameAPI.collectibles.listCards()
// CollectibleCard[]
```

Returns the full card catalog for the current game. Pure metadata — no per-user state. Typically called once on game init and cached client-side; the catalog only changes on the next `rundot deploy`.

```ts
interface CollectibleCard {
  cardId: string                                  // = entitlementId on the platform
  storyId: string
  episodeId?: string
  type: 'free' | 'premium' | 'vip' | 'completion'
  rarity: 'common' | 'rare' | 'epic' | 'legendary'
  title: string
  description: string
  artPath: string                                 // CDN-relative; resolve via cdn.resolveAssetUrl
  thumbPath: string
  lockedArtPath?: string
  priceRbOverride?: number                        // premium only
  limitedTimeEnd?: string                         // ISO date, premium only
}
```

To know which cards the player currently owns, intersect this catalog with `RundotGameAPI.entitlements.listEntitlements()` by `cardId === entitlementId`.

### `claimVipCard(seriesId, cardId)`

```ts
const result = await RundotGameAPI.collectibles.claimVipCard('curse_of_marble', 'curse_of_marble_vip')
// { granted: true, cardId: 'curse_of_marble_vip' }
```

Claims a VIP-tier collectible card for the current user. The server validates two preconditions before granting:

1. **Subscription status** — the user must be an active subscriber (RevenueCat-backed). Non-subscribers receive a 403 with the message "Subscription required to claim."
2. **Series completion** — the user must have completed the requested series (the server checks the `series_completed_{seriesId}` stat).

On success the matching card entitlement is granted directly — no rule engine, no client-writable trigger stat involved. The promise resolves with `{ granted: true, cardId }`.

On failure the promise rejects with an `Error` whose message is the server-supplied detail. Typical consumer pattern:

```ts
try {
  const { cardId } = await RundotGameAPI.collectibles.claimVipCard(seriesId, cardId)
  showRevealOverlay(cardId)
} catch (err) {
  showToast(err.message)   // "Subscription required to claim" / "Not eligible to claim this card"
}
```

Each call is a single user-initiated action — direct RPC, no batching.

***

## Authoring cards

Cards live in `.rundot/collectibles.config.json` alongside the rules that grant them. See the [Stats API](/rundot-docs/v5.21.0/readme/stats.md#wiring-stats-to-collectible-grants) for the full schema (cards + rules + series settings) and an end-to-end watch-threshold-grants-card example.

***

## Related

* [Stats API](/rundot-docs/v5.21.0/readme/stats.md) — submit watch / progress stats that trigger rule-driven grants
* [Entitlements API](/rundot-docs/v5.21.0/readme/entitlements.md) — read card ownership, audit grants
* [Shop API](/rundot-docs/v5.21.0/readme/shop.md) — premium card purchase flow


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://series-1.gitbook.io/rundot-docs/v5.21.0/readme/collectibles.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
