Skip to content
IN THIS GUIDE

Jump directly to a signature. The full reference stays below.

GraveMint SDK — method reference ​

@solanadeads/gravemint · version 1.3.0 · install with npm i @solanadeads/gravemint

Signatures and types are generated from the published package. Explanatory prose is edited for clarity. For how to put them together, start with the GraveMint SDK guide.

ts
import { GraveMintClient } from "@solanadeads/gravemint";
const client = new GraveMintClient({ apiKey: "gm_pub_..." });

Contents ​

client.platform ​

First-party only — not usable with a partner key. See Legacy read API.

platform.getCapabilities() ​

GET /platform-capabilities

Public feature-flag snapshot — which chains are enabled, which optional features (raffles, packs) the platform exposes.

ts
getCapabilities(options?: RequestOptions): Promise<PlatformCapabilities>;

platform.getTokenPrices() ​

GET /token-prices

USD pricing snapshot for supported chain natives. Returns a map keyed by symbol — e.g. { SOL: 240, ETH: 0, POL: 0, SUI: 0 }. The sources map tells you which provider each price came from (or "unavailable"). Intended for display, not for settlement-grade pricing.

ts
getTokenPrices(options?: RequestOptions): Promise<TokenPricesResponse>;

client.discovery ​

First-party only — not usable with a partner key. See Legacy read API.

discovery.getFeatured() ​

GET /featured — {success, collections: [...]}

ts
getFeatured(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getLiveMints() ​

GET /live-mints — {success, mints: [...]}

ts
getLiveMints(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getTrending() ​

GET /trending — {success, type, collections: [...]}

ts
getTrending(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getUpcoming() ​

GET /upcoming-mints — {success, mints: [...]}

ts
getUpcoming(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.getRecentSoldouts() ​

GET /recent-soldouts — {collections: [...]} (no success wrapper)

ts
getRecentSoldouts(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.listCollections() ​

GET /collections — {success, collections: [...]}

ts
listCollections(params?: DiscoveryFilters & {
        offset?: number;
        status?: "active" | "ended" | "upcoming";
    }, options?: RequestOptions): Promise<CollectionSummary[]>;

discovery.searchCollections() ​

GET /collections/search?q=... — {success, collections: [...]} (snake_case items).

Min query length 2 (server-enforced). Items returned use snake_case (short_id, total_supply, blockchain_network) unlike the other discovery feeds which use camelCase. We type this distinctly so the compiler catches the mismatch.

ts
searchCollections(query: string, params?: {
        limit?: number;
    }, options?: RequestOptions): Promise<CollectionSearchResult[]>;

client.collections ​

First-party only — not usable with a partner key. See Legacy read API.

collections.get() ​

GET /collection/:identifier

Full collection record + stats + mint phases + server time. The response is a discriminated union — when the collection isn't fully deployed yet the server returns a pre-launch envelope (status: 'coming_soon' | 'deploying' | 'maintenance') instead of the full record. Branch on ('status' in result) or check for the presence of result.stats.

ts
get(identifier: CollectionIdentifier, options?: RequestOptions): Promise<CollectionResponse>;

collections.getGallery() ​

GET /collection/:identifier/gallery

Paginated list of NFTs in the collection.

ts
getGallery(identifier: CollectionIdentifier, params?: {
        limit?: number;
        offset?: number;
        mintedOnly?: boolean;
    }, options?: RequestOptions): Promise<RecentMint[]>;

collections.getRecentlyMinted() ​

GET /collection/:identifier/recently-minted

Live mint activity. Returns minter wallets (publicly on-chain).

ts
getRecentlyMinted(identifier: CollectionIdentifier, params?: {
        limit?: number;
    }, options?: RequestOptions): Promise<RecentMint[]>;

client.nfts ​

First-party only — not usable with a partner key. See Legacy read API.

nfts.get() ​

GET /nft/:identifier

Single NFT record. Identifier is mint_address (Solana), contract:tokenId (EVM), or numeric token id. UUIDs rejected.

ts
get(identifier: NftIdentifier, options?: RequestOptions): Promise<RecentMint>;

client.bounty ​

First-party only — not usable with a partner key. See Legacy read API.

bounty.getSummary() ​

ts
getSummary(identifier: CollectionIdentifier, options?: RequestOptions): Promise<BountySummary>;

bounty.getPrizes() ​

ts
getPrizes(identifier: CollectionIdentifier, options?: RequestOptions): Promise<BountyPrizesResponse>;

client.codes ​

First-party only — not usable with a partner key. See Legacy read API.

codes.validate() ​

POST /code/validate

Check whether a claim code is valid. Does NOT consume the code.

Note: this endpoint does NOT use the {success:true} envelope. The response shape is {valid: boolean, error?: string, ...}.

ts
validate(code: string, options?: RequestOptions): Promise<CodeValidationResult>;

client.v1 ​

Works with a partner key.

The versioned contract: everything needed to BUILD a mint page.

v1.collection() ​

Everything needed to render the drop.

ts
collection(identifier: string): Promise<V1Collection>;

v1.eligibility() ​

Checks whether a wallet meets a phase’s requirements, including NFT holdings and allowlists. The response keeps eligibility separate from the time window: also check phase.hasStarted and phase.hasEnded before enabling minting.

ts
eligibility(collectionId: string, phaseId: string, walletAddress: string): Promise<V1Eligibility>;

The drop's NFTs that can still be minted, paginated. Minted NFTs are not included — use allMinted() for those. limit defaults to 100.

Scoped server-side to the collections your key covers, so this can only ever return your own drop — a key issued for another collection gets a 403 COLLECTION_NOT_IN_SCOPE, not an empty list.

ts
gallery(identifier: string, params?: {
        limit?: number;
        offset?: number;
    }): Promise<V1Gallery>;

v1.recentlyMinted() ​

The live "just minted" feed for the drop — the same one gravemint.io renders.

ts
recentlyMinted(identifier: string, params?: {
        limit?: number;
    }): Promise<V1RecentlyMinted>;

v1.bounty() ​

Bounty summary for the drop, when it has one.

ts
bounty(identifier: string): Promise<V1BountySummary>;

v1.bountyPrizes() ​

The bounty's public prize table.

ts
bountyPrizes(identifier: string): Promise<V1BountyPrizes>;

v1.walletMints() ​

Returns minted and remaining counts per phase, including bonus mints such as BOGO and bounty mints. Use this response for wallet counts; eligibility uses separate allocation rules.

ts
walletMints(collectionId: string, walletAddress: string): Promise<V1WalletMints>;

v1.claimCodes() ​

Does this drop use claim codes, and of what kind?

ts
claimCodes(collectionId: string): Promise<V1ClaimCodes>;

v1.claimCodeBenefits() ​

What a specific wallet is already entitled to from codes it has redeemed.

ts
claimCodeBenefits(collectionId: string, walletAddress: string, params?: {
        phaseId?: string;
        blockchain?: string;
        packTierId?: string;
    }): Promise<V1ClaimCodeBenefits>;

v1.validateClaimCode() ​

Check a code against THIS drop.

Scoped on purpose. The code is resolved and its own collection compared with the one you name, and a code belonging to another drop answers exactly as an unknown code does — so this can never be used to discover that a code exists elsewhere.

ts
validateClaimCode(collectionId: string, code: string): Promise<V1ClaimCodeCheck>;

v1.tokenPrices() ​

Platform token prices — what an SPL-priced drop is worth in USD. Global, no drop.

ts
tokenPrices(): Promise<V1TokenPrices>;

v1.peggedPrice() ​

The resolved amount for a USD- or native-pegged phase, on its own — e.g. to refresh a live peg without re-reading the whole drop. phase.priceDisplay on the collection response already resolves pegs for display. (v1 phases carry no raw price field.)

ts
peggedPrice(phaseId: string): Promise<V1PeggedPrice>;

v1.dutchPrice() ​

The live price of a dynamic Dutch phase, which moves with time.

ts
dutchPrice(phaseId: string): Promise<V1DutchPrice>;

v1.availability() ​

Supply counts for the drop (see V1Availability): fine for a progress bar, not for deciding a drop is sold out. prepare is what actually allocates supply.

ts
availability(collectionId: string): Promise<V1Availability>;

v1.traits() ​

Trait names and values, for filtering a gallery.

ts
traits(identifier: string): Promise<V1Traits>;

v1.allMinted() ​

Returns one page of minted items. limit defaults to 20 and is capped at 100. Increment offset until hasMore is false. Use newest or oldest for a full paginated history; popular ranks within each page.

ts
allMinted(identifier: string, params?: {
        limit?: number;
        offset?: number;
        sort?: "newest" | "oldest" | "popular";
        search?: string;
    }): Promise<V1AllMinted>;

v1.topHolders() ​

Largest holders — social proof for a mint page.

ts
topHolders(identifier: string): Promise<V1TopHolders>;

v1.topMinters() ​

Who minted the most.

ts
topMinters(identifier: string): Promise<V1TopMinters>;

v1.version() ​

What the API is; useful for degrading deliberately rather than on a 404.

ts
version(): Promise<{
        version: string;
        stability: string;
        docs: string;
    }>;

client.mint ​

Works with a partner key.

prepare -> sign -> execute. You sign; GraveMint broadcasts.

mint.prepare() ​

Ask GraveMint to price, check eligibility, reserve supply and build the transaction.

ts
prepare(input: PrepareMintInput): Promise<PreparedMint>;

mint.execute() ​

Submits the signed transaction through GraveMint. The default timeout is 130 seconds; a configured client-wide timeoutMs takes precedence. Execute is not retried automatically by default. An uncertain result requires checking the existing mint before submitting again. Override options.maxRetries only with application-level outcome handling.

ts
execute(input: {
        sessionId: string;
        signedTransaction: string;
    }, options?: RequestOptions): Promise<V1ExecuteResult>;

mint.executeBatch() ​

Hand back SEVERAL signed transactions at once.

Required for any quantity above 1: every Solana standard mints exactly one NFT per transaction (maxPerTx = 1 for mpl-core, legacy and compressed), so a prepare for N returns N transactions and N sessions.

ts
executeBatch(entries: Array<{
        sessionId: string;
        signedTransaction: string;
    }>, options?: RequestOptions): Promise<V1ExecuteBatchResult>;

mint.mint() ​

Coordinates preparation, signing and execution. Check capabilities.supportedBySurface first and offer mintUrl for collections outside the core flow. Throws if preparation returns no transaction to sign.

ts
mint(input: PrepareMintInput & {
        signer: TransactionSigner;
    }, 
    /** Applied to the EXECUTE call (timeout, signal). Prepare uses the client defaults. */
    executeOptionsOverride?: RequestOptions): Promise<MintResult>;

Functions ​

fromHttpStatus ​

Map an HTTP status to the right error class. Used internally by the HTTP transport. The body is the parsed JSON response if available.

ts
export declare function fromHttpStatus(status: number, message: string, body: unknown, requestId?: string): GraveMintError;

isBatchMintResult ​

True when mint() minted more than one NFT and answered with the batch shape.

ts
export declare function isBatchMintResult(result: MintResult): result is V1MintBatchOutcome;

isUuid ​

Input validation. Catches obvious bad inputs client-side to save round-trips and rate-limit budget. Server-side validation is still authoritative.

Returns true if the string looks like a UUID.

ts
export declare function isUuid(s: string): boolean;

looksLikeAddress ​

Returns true if the string looks like a valid on-chain address (any chain).

ts
export declare function looksLikeAddress(s: string): boolean;

normalizeClaimCode ​

Normalize a claim code (trim, then upper-case), rejecting only what can never be a code: a non-string, or something under 4 or over 1024 UTF-16 code units after trimming and upper-casing. Format-level only; existence is the server's answer.

Deliberately permissive. This used to allow only 6-40 characters of A-Z, 0-9 and '-', and so rejected real codes before the server was ever asked: imported codes of 4-5 or 41-64 characters, codes with _ or ., and generated codes whose prefix has a space or an accented letter. Seven such codes are live (measured 2026-09-23). A client check stricter than the server can only produce false rejections.

NOTE for senders: the server upper-cases the code itself. codes.validate() sends the TRIMMED code rather than this upper-cased form, because toUpperCase() differs across JS engines' Unicode versions for a handful of letters, and the server's answer must not depend on the caller's runtime.

ts
export declare function normalizeClaimCode(code: unknown): string;

sanitize ​

Response sanitization. Removes PII and internal identifiers before returning data to SDK consumers.

Policy ("standard"):

  • STRIP: emails, IP addresses, push subscriptions, fingerprints, internal UUIDs (any field literally named id at any depth, plus known UUID-shaped fields)
  • KEEP: wallet addresses (publicly on-chain), display names, mint addresses, short_ids, content URLs.

The sanitizer is conservative — when in doubt, it strips. Consumers who need the raw response can set client.options.sanitize = false per-request, but doing so re-exposes UUIDs and is documented as unsafe for public callers.

Returns a deep-cleaned copy of the input. Does not mutate. Safe to call on arbitrary JSON-shaped data (arrays, objects, primitives, null).

ts
export declare function sanitize<T = unknown>(value: T): T;

validateCollectionIdentifier ​

Validate a collection identifier. Accepts a short_id or an on-chain address. Explicitly rejects UUIDs — the SDK does not expose internal IDs.

Throws ValidationError on bad input.

ts
export declare function validateCollectionIdentifier(id: unknown): string;

validateLimit ​

Validate a pagination limit against a max. Returns the clamped value.

ts
export declare function validateLimit(limit: unknown, max: number, fallback: number): number;

validateNftIdentifier ​

Validate an NFT identifier. Accepts a mint_address, a contract:tokenId pair, or a numeric token ID. Rejects UUIDs.

ts
export declare function validateNftIdentifier(id: unknown): string;

validateOffset ​

Validate an offset.

ts
export declare function validateOffset(offset: unknown): number;

validateWalletAddress ​

Validate a wallet address. Returns the input unchanged on success.

ts
export declare function validateWalletAddress(addr: unknown): string;

Classes ​

AuthError ​

Server returned 401 / 403 — the request was refused. NOT only a key or origin problem: on v1 a 403 is also a held or blocked wallet, a banned asset, a missing key scope or a key class used on the wrong network. Branch on code, not on the class.

ts
export declare class AuthError extends GraveMintError {
}

ConfigError ​

Configuration error — bad apiKey, missing baseUrl, invalid options.

ts
export declare class ConfigError extends GraveMintError {
}

GraveMintError ​

GraveMint SDK error hierarchy.

All errors thrown by the SDK extend GraveMintError. Catch this base class to handle any SDK failure; catch a subclass to react to a specific failure mode (rate limit, auth, bad input, etc.).

ts
export declare class GraveMintError extends Error {
    readonly name: string;
    readonly status?: number | undefined;
    readonly code?: string | undefined;
    readonly requestId?: string | undefined;
    /**
     * The server's parsed error body, when it sent JSON. This is where a code's
     * documented extra fields live — e.g. `reason` / `blockedUntil` / `permanent` on
     * `WALLET_BLOCKED`, `retryAfter` on `COLLECTION_RATE_LIMITED`.
     * RAW and unsanitized (error bodies are not run through the response sanitizer), and
     * its shape varies by route — read the documented field you need, do not forward it
     * whole. Undefined for errors raised on the client.
     */
    readonly details?: unknown;
    constructor(message: string, opts?: {
        status?: number;
        code?: string;
        requestId?: string;
        details?: unknown;
        cause?: unknown;
    });
}

HttpClient ​

ts
export declare class HttpClient {
    private readonly baseUrl;
    private readonly apiKey;
    private readonly timeoutMs;
    private readonly maxRetries;
    /** The client-wide timeout the CALLER set, or undefined when it is the default. */
    readonly configuredTimeoutMs: number | undefined;
    private readonly sanitize;
    private readonly fetchImpl;
    private readonly clientId;
    private readonly limiter;
    constructor(opts: HttpClientOptions);
    get<T>(path: string, options?: RequestOptions): Promise<T>;
    post<T>(path: string, body: unknown, options?: RequestOptions): Promise<T>;
    private request;
}

MintOutcomeUnknownError ​

mint() submitted signed transactions but could not confirm the outcome. This can follow a timeout, connection loss, server error or an already-processing response. The mint may still complete. Retain sessionIds, display the message and check the wallet before starting another mint. This SDK error uses code: "OUTCOME_UNKNOWN".

ts
export declare class MintOutcomeUnknownError extends GraveMintError {
    readonly sessionIds: string[];
    constructor(sessionIds: string[], opts?: {
        cause?: unknown;
        requestId?: string;
    });
}

NetworkError ​

Network failure — DNS, TCP, TLS, fetch timeout.

ts
export declare class NetworkError extends GraveMintError {
}

NotFoundError ​

Server returned 404 — resource does not exist.

ts
export declare class NotFoundError extends GraveMintError {
}

RateLimitError ​

Server returned 429 — rate limit hit. retryAfterMs is the server-suggested wait.

ts
export declare class RateLimitError extends GraveMintError {
    readonly retryAfterMs: number;
    constructor(message: string, retryAfterMs: number, opts?: {
        status?: number;
        code?: string;
        requestId?: string;
        details?: unknown;
    });
}

ServerError ​

Server returned 5xx after exhausting retries.

ts
export declare class ServerError extends GraveMintError {
}

TimeoutError ​

Request was aborted (client-side timeout or external AbortSignal).

ts
export declare class TimeoutError extends GraveMintError {
}

TokenBucketRateLimiter ​

ts
export declare class TokenBucketRateLimiter {
    private tokens;
    private lastRefill;
    private readonly refillRate;
    private readonly capacity;
    private readonly queue;
    /** The ONE pending wake-up while anyone is queued. */
    private timer;
    constructor(config: RateLimitConfig);
    /**
     * Acquire one token. Resolves immediately if one is available and nobody is waiting, otherwise
     * queues (first come, first served). Honors `signal` — aborting rejects the wait.
     *
     *: this used to arm one timer per waiter, and a woken waiter that found no token
     * re-queued itself WITHOUT a timer — once the armed timers had fired, everyone still queued hung
     * forever (12 concurrent calls at the defaults: 11 resolved). Now a single drain loop owns the
     * queue: one timer while anyone waits, each tick hands out every available token in order, then
     * re-arms for the next.
     */
    acquire(signal?: AbortSignal): Promise<void>;
    /** Arm the single wake-up for the time the next token needs, if anyone is waiting. */
    private schedule;
    /** Hand every available token to the queue in order, then re-arm for whoever is left. */
    private drain;
    private refill;
}

ValidationError ​

Input validation error — bad address, malformed identifier, out-of-range limit.

ts
export declare class ValidationError extends GraveMintError {
}

Types ​

Blockchain ​

Public-facing types for the GraveMint SDK.

ts
export type Blockchain = "solana-mainnet" | "solana-devnet" | "ethereum-mainnet" | "polygon-mainnet" | "base-mainnet" | "arbitrum-mainnet" | "optimism-mainnet" | "bsc-mainnet" | "avalanche-mainnet" | "apechain-mainnet" | "abstract-mainnet" | "cronos-mainnet" | "berachain-mainnet" | "sui-mainnet" | "sui-testnet" | string;

BountyPrize ​

ts
export interface BountyPrize {
    kind: "milestone_group" | "individual";
    name: string;
    bounties: Array<{
        mint_count?: number;
        name: string;
        nft?: {
            mint_address: string | null;
            name: string | null;
            image_url: string | null;
        };
        token?: {
            symbol: string;
            amount: string | number;
            decimals: number;
        };
        grant?: string;
        status: "open" | "claimed" | "expired";
        winner_wallet?: string | null;
        claimed_at?: string | null;
    }>;
}

BountyPrizesResponse ​

ts
export interface BountyPrizesResponse {
    enabled: boolean;
    show_occurrence?: boolean;
    show_winners?: boolean;
    sections?: BountyPrize[];
}

BountySummary ​

ts
export interface BountySummary {
    enabled: boolean;
    total_milestones?: number;
    triggered?: number;
    total_funded_usd?: number;
    next_threshold?: number | null;
    [key: string]: unknown;
}

CodeValidationResult ​

/code/validate does NOT use the {success:true} envelope.

ts
export interface CodeValidationResult {
    valid: boolean;
    /** Server-side reason string when valid=false. */
    error?: string;
    /** `external` and `nft_redemption` are real grant types the server returns. */
    grantType?: "whitelist" | "free_mint" | "discount" | "external" | "nft_redemption";
    /** Number of mints the code grants, when it grants any. */
    mintAllocation?: number | null;
    /** Percentage discount — the field the server actually sends. */
    discountPercentage?: number | null;
    /** Fixed-amount discount, in the drop's currency. */
    discountFixed?: number | null;
    usesRemaining?: number | null;
    /**
     * @deprecated The server has never sent this name — it sends `discountPercentage`.
     * Always undefined; kept so existing code still compiles.
     */
    discountPercent?: number;
    collectionShortId?: string | null;
    collectionAddress?: string | null;
    [key: string]: unknown;
}

CollectionDetail ​

ts
export interface CollectionDetail {
    /** Short, URL-safe public ID (e.g. "deads"). Preferred identifier. */
    shortId: string;
    name: string;
    symbol: string | null;
    bio: string | null;
    description: string | null;
    image: string | null;
    imageFocalY: number;
    bannerImage: string | null;
    bannerFocalY: number;
    externalUrl: string | null;
    blockchain: Blockchain;
    collectionAddress: string | null;
    contractAddress: string | null;
    nftStandard: "mpl-core" | "legacy" | "compressed" | "erc721" | "erc1155" | string;
    contentType: "image" | "audio" | "video" | string;
    enableGalleryMode: boolean;
    allowNftSelection: boolean;
    editionType: "unique" | "limited" | "limited_edition" | string;
    maxEditionSupply: number | null;
    allowImageDownload: boolean;
    showTraitRarity: boolean;
    twitter: string | null;
    discord: string | null;
    telegram: string | null;
    creatorWallet: string | null;
    twitterVerified: boolean;
    discordVerified: boolean;
    creatorVerified: boolean;
    royaltyPercentage: number;
    promoVideoUrl: string | null;
    isGenerative: boolean;
    enableDelayedReveal: boolean;
    isRevealed: boolean;
    roadmap: unknown;
    affiliateEnabled: boolean;
    mintReplaysEnabled: boolean;
    [key: string]: unknown;
}

CollectionDetailResponse ​

ts
export interface CollectionDetailResponse {
    collection: CollectionDetail;
    stats: CollectionStats;
    mintStatus: "upcoming" | "live" | "paused" | "ended" | "sold_out" | string;
    countdown: unknown;
    phases: {
        active: Phase[];
        upcoming: Phase[];
        all: Phase[];
    };
    serverTime: string;
}

CollectionIdentifier ​

ts
export type CollectionIdentifier = string;

CollectionPreLaunchResponse ​

Pre-launch / maintenance variants returned by the same endpoint when the collection is not yet fully deployed. The SDK type for getCollection unions these so callers can branch on status.

ts
export interface CollectionPreLaunchResponse {
    status: "coming_soon" | "deploying" | "maintenance";
    message: string;
    collection: {
        shortId?: string;
        name: string;
        image: string | null;
        blockchain: Blockchain;
        description?: string | null;
        totalSupply?: number;
        mintedCount?: number;
    };
    /** present only when status === 'coming_soon' */
    raffles?: Array<{
        name: string;
        status: string;
        winnerCount: number;
        registrationStart: string;
        registrationEnd: string;
    }>;
    hasClaimCodes?: boolean;
    hasWhitelistCodes?: boolean;
}

CollectionResponse ​

ts
export type CollectionResponse = CollectionDetailResponse | CollectionPreLaunchResponse;

CollectionSearchResult ​

/collections/search returns snake_case unlike the camelCase feeds.

ts
export interface CollectionSearchResult {
    short_id: string;
    name: string;
    symbol: string | null;
    image: string | null;
    image_url: string | null;
    collection_address: string | null;
    contract_address: string | null;
    blockchain_network: Blockchain;
    total_supply: number;
    minted_count: number;
}

CollectionStats ​

ts
export interface CollectionStats {
    totalSupply: number;
    mintedCount: number;
    burnedCount: number;
    availableCount: number | null;
    percentMinted: number;
}

CollectionSummary ​

Discovery-feed summary (camelCase). Used by /featured, /trending, /live-mints, /upcoming-mints, /recent-soldouts.

ts
export interface CollectionSummary {
    shortId: string;
    name: string;
    symbol: string | null;
    bio?: string | null;
    description?: string | null;
    image: string | null;
    imageCropPosition?: string;
    imageFocalY?: number;
    bannerImage?: string | null;
    bannerFocalY?: number;
    blockchain: Blockchain;
    collectionAddress?: string | null;
    contractAddress?: string | null;
    isGenerative?: boolean;
    promoVideoUrl?: string | null;
    verified?: boolean;
    socialVerified?: boolean;
    stats: {
        totalSupply: number;
        mintedCount: number;
        burnedCount?: number;
        availableCount?: number;
        percentMinted?: number;
        totalVolume?: number;
        floorPrice?: number;
        mintPrice?: number;
        mintCurrency?: string;
        isSoldOut?: boolean;
    };
    badges?: {
        hasBounties?: boolean;
        hasAffiliates?: boolean;
    };
    trending?: {
        rank?: number;
        mints24h?: number;
        mints7d?: number;
        mints30d?: number;
        volume24h?: number;
    };
    /** Present on /recent-soldouts */
    deployedAt?: string;
    firstMintedAt?: string;
    effectiveSoldOutAt?: string;
    soldOutAt?: string;
    selloutSeconds?: number;
    selloutLabel?: string;
    /** Present on /upcoming-mints */
    phase?: {
        name: string;
        type: string;
        price: number;
        currency: string;
        paymentToken: unknown;
        [k: string]: unknown;
    };
}

DiscoveryFilters ​

ts
export interface DiscoveryFilters {
    /** Limit results. Capped at 100. Default 20. */
    limit?: number;
    /** Filter by blockchain. Server ignores unknown values. */
    blockchain?: Blockchain;
}

GraveMintClientOptions ​

ts
export interface GraveMintClientOptions {
    /**
     * API key issued by GraveMint. Sent on every request as `X-API-Key`.
     * Required — even browser-side, the server enforces origin + key.
     */
    apiKey: string;
    /**
     * Override the API base URL. Default: production.
     * The public router is mounted at `/gravemint/public`; if you point at a
     * different host, keep the same path suffix.
     */
    baseUrl?: string;
    /**
     * Override the base URL for the versioned contract (`gm.v1`, `gm.mint`).
     *
     * You only need this when routing through your own proxy, or when `baseUrl`
     * points somewhere whose shape we cannot infer. If you set `baseUrl` to a
     * host ending in `/public`, this is derived for you by swapping the suffix.
     * If it ends in anything else we do NOT guess — the constructor throws,
     * because a wrong base on the mint path is a 404 discovered by a collector
     * mid-transaction rather than by you at startup.
     */
    v1BaseUrl?: string;
    /** Per-request timeout (ms). Default 30_000. */
    timeoutMs?: number;
    /** Max retry attempts on 429 / 5xx / network. Default 3. */
    maxRetries?: number;
    /**
     * Strip PII / internal UUIDs from responses. Default true.
     * Setting `false` re-exposes internal identifiers — not recommended for
     * production callers.
     */
    sanitize?: boolean;
    /**
     * Client-side rate limit. Pass `null` to disable. Default 5 RPS sustained,
     * 10 RPS burst (300/min) — below the server's published 600 reads/min/IP floor
     * (README "Rate limits"). Mints have their own, lower per-key budget.
     */
    rateLimit?: RateLimitConfig | null;
    /**
     * Custom fetch implementation. Defaults to `globalThis.fetch` (Node 18+,
     * browsers, Bun, Deno). On older Node, pass `undici.fetch` or `node-fetch`.
     */
    fetch?: typeof fetch;
}

HttpClientOptions ​

ts
export interface HttpClientOptions {
    baseUrl: string;
    apiKey: string;
    /** Default timeout per request in milliseconds. Default 30s. */
    timeoutMs?: number;
    /** Max retry attempts on 429 / 5xx / network. Default 3. */
    maxRetries?: number;
    /** Sanitize responses (strip PII / UUIDs). Default true. */
    sanitize?: boolean;
    /** Client-side rate limit. Pass `null` to disable. Default {requestsPerSecond:5,burst:10}. */
    rateLimit?: RateLimitConfig | null;
    /** Custom fetch impl. Defaults to globalThis.fetch. */
    fetch?: typeof fetch;
    /** SDK version, sent as `x-gm-client`. Set by index.ts. */
    sdkVersion?: string;
}

MintResult ​

mint(): a single result for quantity 1, a batch outcome (with results) above that.

ts
export type MintResult = V1ExecuteResult | V1MintBatchOutcome;

NftIdentifier ​

ts
export type NftIdentifier = string;

Phase ​

ts
export interface Phase {
    name: string;
    type: "public" | "whitelist" | "presale" | "dutch_auction" | "dynamic_dutch" | string;
    status: "active" | "upcoming" | "ended";
    price: number;
    priceCurrency?: string | null;
    paymentTokenSymbol?: string | null;
    paymentTokenAddress?: string | null;
    paymentTokenDecimals?: number | null;
    startDate: string;
    endDate: string | null;
    maxPerWallet?: number | null;
    maxPerTransaction?: number | null;
    globalMintLimit?: number | null;
    timeUntilStart?: number | null;
    timeUntilEnd?: number | null;
    [key: string]: unknown;
}

PlatformCapabilities ​

ts
export interface PlatformCapabilities {
    solana?: {
        mplCore: boolean;
        legacy: boolean;
        compressed: boolean;
    };
    evm?: {
        erc721: boolean;
        erc1155: boolean;
    };
    sui?: {
        enabled: boolean;
        mainnet: boolean;
        testnet: boolean;
        editions: boolean;
    };
    features?: Record<string, boolean>;
    raffles?: {
        twitterRequirements: boolean;
        maxActiveRaffles: number;
    };
    enabledChains?: Record<string, boolean>;
    [key: string]: unknown;
}

PlatformSetting ​

ts
export interface PlatformSetting {
    setting_key: string;
    setting_value: unknown;
    is_public: boolean;
}

PreparedMint ​

Response from /v1/prepare-mint. v1 returns transactions[] and sessions[], including for quantity one. Use sessions[0].sessionId for the first transaction. The optional top-level sessionId supports legacy proxy responses.

ts
export interface PreparedMint {
    success: boolean;
    /** Legacy single-shape only. On v1 this is undefined — use `sessions[0]`. */
    sessionId?: string;
    sessions?: Array<{
        sessionId: string;
        nftIds: string[];
        nftCount: number;
    }>;
    /** What was asked for. Compare with `totalQuantity`: see `quantityAdjusted`. */
    requestedQuantity?: number;
    /**
     * TRUE when fewer NFTs were prepared than requested (a wallet or phase limit, or supply).
     * Tell the collector — they are about to sign for fewer than they asked for.
     */
    quantityAdjusted?: boolean;
    /** Why, in a sentence, when the reason is a user-facing limit. */
    adjustmentReason?: string | null;
    /** Session lifetime in ms — use this for a countdown (clock-skew safe), not `expiresAt`. */
    expiresInMs?: number;
    transactions?: Array<{
        transaction?: string | null;
        isVersionedTransaction?: boolean;
        nftCount?: number;
    }>;
    totalQuantity?: number;
    /**
     * The AUTHORITATIVE price breakdown. This — not `priceDisplay` — is what the
     * collector actually pays. `priceDisplay` is for rendering the phase before a
     * mint is requested; the real figure only exists once GraveMint has run the
     * full cascade and reserved what the price depends on.
     */
    pricing?: Record<string, unknown>;
    bogo?: Record<string, unknown> | null;
    /** The session expires. Do not hold a signature and submit it later. */
    expiresAt?: string;
    chainType?: string;
}

PrepareMintInput ​

ts
export interface PrepareMintInput {
    /**
     * The drop to mint from: its UUID (`drop.collection.id`), `shortId`, or on-chain address. A
     * non-UUID is resolved within your key's own drops only.
     */
    collectionId: string;
    phaseId: string;
    quantity: number;
    walletAddress: string;
    /** Referral attribution. Validated server-side; ignored if the drop has none. */
    affiliateCode?: string | null;
    /**
     * The connected wallet's display name, e.g. `wallet.adapter.name` ("Phantom", "Solflare").
     * Analytics only: it fills the creator's wallet breakdown, which otherwise reads "Unknown"
     * for every mint from your site. Invalid values are dropped server-side.
     */
    walletProvider?: string | null;
}

PriceDisplay ​

The four display-price variants returned by GraveMint’s price resolver.

ts
export type PriceDisplay = {
    kind: "amount";
    amount: number;
    currency: string;
    isFree: boolean;
    approximate?: boolean;
} | {
    kind: "range";
    min: number;
    max: number;
    currency: string;
} | {
    kind: "hidden";
} | {
    kind: "unknown";
};

RateLimitConfig ​

Client-side rate limiter (token bucket).

Why this exists: the server enforces its own limits (~80 reads/min/IP by default) and returns 429 on overage. Hitting 429s wastes round-trips and triggers bot-detection penalties. The SDK's client-side limiter keeps callers under a configurable ceiling and serializes overage requests into a queue rather than firing them off and getting blocked.

Configurable via GraveMintClientOptions.rateLimit: { requestsPerSecond: 5, burst: 10 } // 5 RPS sustained, 10 RPS short burst

Setting rateLimit: null disables the client-side limiter (server still enforces). Default: { requestsPerSecond: 5, burst: 10 }.

ts
export interface RateLimitConfig {
    /** Sustained request rate per second. */
    requestsPerSecond: number;
    /** Max burst — tokens accumulated when idle. */
    burst: number;
}

RecentMint ​

ts
export interface RecentMint {
    tokenId: number | null;
    mintAddress: string | null;
    name: string | null;
    image: string | null;
    previewImage: string | null;
    fullImageUrl: string | null;
    generationStatus: string | null;
    ownerWallet: string | null;
    mintedAt: string;
    isRevealed: boolean;
    attributes: Array<{
        trait_type: string;
        value: string | number;
    }>;
    editionNumber: number | null;
    maxEditionSupply: number | null;
    isMasterEdition: boolean;
    currentEditionSupply?: number;
}

RequestOptions ​

ts
export interface RequestOptions {
    /** Per-request timeout override (ms). */
    timeoutMs?: number;
    /**
     * Per-request retry override. `execute` uses 0: re-POSTing signed transactions after an
     * ambiguous failure makes the server answer ALREADY_PROCESSING / ALREADY_COMPLETED /
     * SESSION_NOT_FOUND, which read as definite failures and hide that the first attempt
     * may have landed.
     */
    maxRetries?: number;
    /** Per-request signal for cancellation. Combined with timeout signal. */
    signal?: AbortSignal;
    /** Override sanitization for this request only. */
    sanitize?: boolean;
    /** Query-string params (values stringified). Undefined/null entries are skipped. */
    query?: Record<string, string | number | boolean | undefined | null>;
    /** JSON body for POST. */
    body?: unknown;
}

TokenPricesResponse ​

ts
export interface TokenPricesResponse {
    /** Map of native-chain symbol → USD price. e.g. { SOL: 240, ETH: 0, POL: 0, SUI: 0 }. */
    prices: Record<string, number>;
    /** Per-symbol source (e.g. "coingecko", "jupiter", "unavailable"). */
    sources: Record<string, string>;
    /** ms epoch when the snapshot was taken server-side. */
    timestamp: number;
    /** Server-side cache TTL in seconds. */
    cacheTTL: number;
}

TransactionSigner ​

Anything that can sign a base64 transaction and hand back a base64 transaction.

Deliberately the narrowest possible surface: the less this SDK knows about your wallet, the fewer ways it can break when you change it.

ts
export interface TransactionSigner {
    signTransaction(base64Transaction: string): Promise<string>;
    /**
     * Optional. When present it is used for multi-transaction mints, so the
     * collector approves ONCE instead of N times. `@solana/wallet-adapter`
     * exposes this; a signer without it still works, one prompt per NFT.
     */
    signAllTransactions?(base64Transactions: string[]): Promise<string[]>;
}

V1AllMinted ​

allMinted() — one page. Keep requesting with offset until hasMore is false.

ts
export interface V1AllMinted {
    success: true;
    nfts: V1MintedItem[];
    /** Matching minted items in the whole drop. */
    total: number;
    hasMore: boolean;
    offset: number;
    /** The page size actually applied (the server caps it at 100). */
    limit: number;
    collection: {
        id: string;
        shortId: string | null;
        name: string | null;
        twitterHandle: string | null;
    };
    allowImageDownload: boolean;
}

V1Availability ​

availability() — read fresh, the same counts as getCollection().stats. available is total - minted; minted includes burned (a burned NFT was minted). An NFT held by a mint still in progress counts as available until that mint completes, so available can briefly read high: use it for a progress bar, and let prepare decide whether supply remains. available is null for an open-ended generative drop, which has no fixed supply. percentMinted is 0-100.

ts
export interface V1Availability {
    success: true;
    collectionId: string;
    stats: {
        total: number;
        minted: number;
        burned: number;
        available: number | null;
        percentMinted: number;
    };
}

V1BatchTransactionResult ​

One transaction's outcome inside a batch. success: false carries error (a sentence) and usually errorCode. pending: true means it may still land — check the wallet, do not retry. A success carries the minted NFTs under minted (an array here, unlike the single-transaction answer, where minted is a count).

ts
export interface V1BatchTransactionResult {
    sessionId: string;
    success: boolean;
    error?: string | null;
    errorCode?: string;
    pending?: boolean;
    recovered?: boolean;
    signature?: string;
    mintedCount?: number;
    minted?: V1MintedNft[];
    [k: string]: unknown;
}

V1BountyInventoryItem ​

ts
export interface V1BountyInventoryItem {
    id: string;
    name: string | null;
    image_url: string | null;
    mint_address: string | null;
    state: "unassigned" | "assigned" | "claimed";
    claimed_winner?: string | null;
    claimed_at?: string | null;
}

V1BountyItem ​

One bounty in the prize table. Which of nft / grant / token is set depends on bounty_type.

ts
export interface V1BountyItem {
    id: string;
    name: string | null;
    description: string | null;
    bounty_type: string | null;
    is_claimed: boolean;
    /** A shortened wallet, and only when the creator shows winners. */
    claimed_winner: string | null;
    claimed_at: string | null;
    nft?: {
        name: string | null;
        image_url: string | null;
        mint_address: string | null;
    };
    grant?: {
        label: string;
    };
    /** `amount` is in the token's smallest unit; null when the creator left it unset. */
    token?: {
        symbol: string | null;
        amount: number | null;
        decimals: number;
    };
    trigger?: {
        mint_count: number | null;
        starts_at: string | null;
    };
}

V1BountyPoolSection ​

A recurring prize pool. weight_pct is the chance of each entry per draw.

ts
export interface V1BountyPoolSection {
    id: string;
    name: string | null;
    description: string | null;
    mode: "recurring_pool";
    recur_every_n_mints: number | null;
    recur_max_fires: number | null;
    recur_fires_count: number;
    counter_starts_at: string | null;
    entries: Array<{
        reward_type: string;
        weight_bps: number;
        weight_pct: number;
        payout_amount: number | null;
        token_symbol: string | null;
        token_decimals: number | null;
        nft_pool_label: string | null;
        display_label: string | null;
        display_image_url: string | null;
    }>;
}

V1BountyPrizes ​

bountyPrizes(). enabled: false when the creator does not show the prize table.

ts
export type V1BountyPrizes = {
    success: true;
    enabled: false;
} | {
    success: true;
    enabled: true;
    show_occurrence: boolean;
    show_winners: boolean;
    show_inventory_preview: boolean;
    sections: V1BountySection[];
    pool_sections: V1BountyPoolSection[];
};

V1BountySection ​

ts
export type V1BountySection = {
    kind: "milestone_group";
    id: string;
    name: string;
    counter_starts_at?: string | null;
    bounties: V1BountyItem[];
} | {
    kind: "individual";
    id?: string;
    name: string;
    bounties: V1BountyItem[];
} | {
    kind: "inventory_preview";
    id?: string;
    name: string;
    items: V1BountyInventoryItem[];
};

V1BountySummary ​

bounty(). total_pool and pool_raw are in each token's smallest unit.

ts
export interface V1BountySummary {
    success: true;
    remaining_bounties: number;
    free_mint_bounties: number;
    total_pool: number;
    /** One symbol, `"tokens"` when the pool spans several, or null when there is none. */
    token_symbol: string | null;
    tokens: Array<{
        address: string;
        symbol: string | null;
        decimals: number;
        pool_raw: number;
    }>;
    has_bounties: boolean;
}

V1Capabilities ​

Describes the features a collection requires and whether the core mint flow supports them. When supportedBySurface is false, offer the hosted mintUrl when available.

ts
export interface V1Capabilities {
    requiresFeatures: string[];
    supportedBySurface: boolean;
    mintUrl: string | null;
}

V1ClaimCodeBenefits ​

claimCodeBenefits(). All zeros with source: null when the wallet has redeemed nothing. success is false (and everything zero) when the server could not look it up, so do not read zeros as "no benefits" without checking it.

ts
export interface V1ClaimCodeBenefits {
    success: boolean;
    freeMints: number;
    discountPercent: number;
    discountFixed: number;
    discountRemaining?: number;
    packTierId?: string | null;
    source: null | {
        type: "free_mint";
        batchName: string | null;
        allocation: number | null;
        used: number;
        batchId?: string;
        packTierId?: string | null;
    } | {
        type: "discount";
        batchName: string | null;
        percent?: number;
        fixed?: number;
        allocation: number;
        used: number;
        remaining: number;
        batchId?: string;
    };
}

V1ClaimCodeCheck ​

validateClaimCode() — the v1 answer. A code for another drop answers { valid: false }.

ts
export interface V1ClaimCodeCheck {
    valid: boolean;
    grantType?: "whitelist" | "free_mint" | "discount" | "external" | "nft_redemption";
    mintAllocation?: number | null;
    discountPercentage?: number | null;
    discountFixed?: number | null;
    usesRemaining?: number | null;
    [k: string]: unknown;
}

V1ClaimCodes ​

claimCodes().

ts
export interface V1ClaimCodes {
    hasClaimCodes: boolean;
    hasDiscountCodes: boolean;
    hasWhitelistCodes: boolean;
    hasPackCodes: boolean;
}

V1Collection ​

ts
export interface V1Collection {
    collection: {
        id: string;
        shortId: string | null;
        name: string | null;
        symbol: string | null;
        description: string | null;
        image: string | null;
        bannerImage: string | null;
        chain: string | null;
        collectionAddress: string | null;
        contractAddress: string | null;
        externalUrl: string | null;
        twitter: string | null;
        discord: string | null;
        isVerified: boolean;
    };
    stats: {
        totalSupply: number;
        mintedCount: number;
        availableCount: number;
        percentMinted: number;
    };
    /** `all` INCLUDES ended phases, for displaying the full schedule. */
    phases: {
        all: V1Phase[];
        active: V1Phase[];
        upcoming: V1Phase[];
    };
    capabilities: V1Capabilities;
    /** GraveMint's clock. Render countdowns against this, never `Date.now()`. */
    serverTime: string;
}

V1DutchPrice ​

dutchPrice(). price is the live figure; the rest explains how it got there.

ts
export interface V1DutchPrice {
    success: true;
    price: number;
    basePrice: number;
    surgePremium: number;
    decayAmount: number;
    isSurging: boolean;
    mintsInWindow: number;
    totalMints: number;
    surgeThreshold: number;
    floorPrice: number;
    ceilingPrice: number;
    surgeWindowMinutes: number;
    startedAt: string | null;
    lastMintAt: string | null;
    /** The phase's Dutch-auction settings, as configured. */
    config: Record<string, unknown>;
    /** Milliseconds since the epoch. */
    timestamp: number;
}

V1Eligibility ​

ts
export interface V1Eligibility {
    gated: boolean;
    meetsRequirements?: boolean;
    onAllowlist?: boolean | null;
    spotsAllocated?: number | null;
    spotsRemaining?: number | null;
    requirements?: Array<{
        type: string;
        met: boolean;
        description: string;
    }>;
    /**
     * `name` can be null at runtime for an unnamed phase; it stays typed `string` because
     * narrowing a published type is a breaking change here. `startDate`/`endDate` are ISO
     * strings or null (open-ended).
     */
    phase: {
        id: string;
        name: string;
        hasStarted: boolean;
        hasEnded: boolean;
        startDate?: string | null;
        endDate?: string | null;
    };
    message?: string;
}

V1ExecuteBatchResult ​

executeBatch(). ALWAYS answered with HTTP 200, even when every transaction failed, and success is true when ANY landed — read results[] and the counts, never success alone.

ts
export interface V1ExecuteBatchResult {
    success: boolean;
    results: V1BatchTransactionResult[];
    totalMinted: number;
    successfulTransactions: number;
    failedTransactions: number;
    executionTimeMs: number;
    /** Kept from the 1.2.x `Record<string, unknown>` type, so existing reads still compile. */
    [k: string]: unknown;
}

V1ExecuteResult ​

execute() — the server's answer for ONE signed transaction.

A failure is thrown, never returned. Fields other than the reveal settings and totalCost / currency can be absent: a session the server recovered after an interruption answers with the count only (minted) and no signature or nfts.

ts
export interface V1ExecuteResult {
    success: true;
    /** How many NFTs this transaction minted (a COUNT — the NFTs themselves are in `nfts`). */
    minted: number;
    signature?: string;
    /** Same value as `signature`. */
    transactionHash?: string;
    chainType: string;
    nfts?: V1MintedNft[];
    /** What the collector paid, in `currency`: mint price plus platform fee. */
    totalCost: number;
    currency: string;
    collectionStats?: {
        totalSupply: number;
        mintedCount: number;
        percentMinted: number;
        isMintedOut: boolean;
    } | null;
    collectionUpdate?: {
        itemsRedeemed: number;
        isFrozen: boolean;
        autoUnfrozen: boolean;
    } | null;
    delayedReveal?: boolean;
    instantRevealAfterMint?: boolean;
    timeDelayedReveal?: boolean;
    revealDelayMinutes?: number | null;
    revealAfter?: string | null;
    revealAnimationUrl: string | null;
    revealTransitionType: string;
    revealAnimationTrigger: string;
    revealTapCaption: string | null;
    isPlatformPaid?: boolean;
    bountyInfo?: {
        directBounties: Array<Record<string, unknown>>;
    } | null;
    /** Never set on a single result — lets `if (res.results)` tell a batch apart. */
    results?: undefined;
    /**
     * 1.2.x typed this result as `Record<string, unknown>`; the index signature keeps code that
     * read other fields that way compiling within 1.x ( review).
     */
    [k: string]: unknown;
}

V1Gallery ​

gallery().

ts
export interface V1Gallery {
    success: true;
    nfts: V1GalleryNft[];
    total: number;
    limit: number;
    offset: number;
}

V1GalleryNft ​

One NFT that can still be minted.

ts
export interface V1GalleryNft {
    id: string;
    /** null for a limited-edition drop, whose editions share one artwork. */
    token_id: number | null;
    name: string | null;
    description: string | null;
    image_url: string | null;
    preview_image_url: string | null;
    cover_art_url: string | null;
    animation_url: string | null;
    /** Always null here: full audio is never public. Use `audio_preview_url`. */
    audio_url: null;
    audio_preview_url: string | null;
    media_type: string | null;
    preview_url: string | null;
    preview_start_time: number | null;
    preview_duration: number | null;
    audio_duration_seconds: number | null;
    mint_status: string | null;
    /** The NFT's metadata attributes, passed through as stored. */
    attributes: unknown;
    is_master_edition: boolean;
    current_edition_supply: number;
    max_edition_supply: number | null;
    edition_number: number | null;
    master_nft_id: string | null;
    /** Set when the NFT is reserved for one wallet. */
    reservation_wallet: string | null;
    reserved_by: string | null;
}

V1LeaderboardEntry ​

One entry on a leaderboard. platformRank is the wallet's GraveLink rank name.

ts
export interface V1LeaderboardEntry {
    rank: number;
    walletAddress: string;
    displayName: string | null;
    avatarUrl: string | null;
    platformRank: string;
    effects: V1ProfileEffect[];
}

V1MintBatchOutcome ​

What mint() returns for a quantity above 1: the batch answer, annotated. When any transaction failed, success is false, partial says whether SOME landed, minted of requested is the tally, and message is a sentence safe to show the collector.

ts
export interface V1MintBatchOutcome extends V1ExecuteBatchResult {
    partial?: boolean;
    requested?: number;
    minted?: number;
    message?: string;
}

V1MintedItem ​

One NFT in a minted feed. Regular drops and limited editions carry different extras, so every field outside the common set is optional.

ts
export interface V1MintedItem {
    id: string;
    /** null for a limited-edition drop — use `editionNumber` there. */
    tokenId: number | null;
    name: string | null;
    image: string | null;
    fullImageUrl: string | null;
    mintAddress: string | null;
    mintedAt: string | null;
    isRevealed: boolean;
    ownerWallet: string | null;
    attributes: unknown[];
    minterDisplayName: string | null;
    minterAvatarUrl: string | null;
    minterEffects: V1ProfileEffect[];
    reactions?: Record<string, number>;
    editionNumber?: number | null;
    packTierName?: string | null;
    rarityRank?: number | null;
    rarityTier?: string | null;
    rarityScore?: number | null;
    bounty?: V1NftBounty | null;
    isCrossChain?: boolean;
    sourceChain?: string | null;
}

V1MintedNft ​

One NFT a successful execute minted.

ts
export interface V1MintedNft {
    nftId?: string;
    /** null for a limited-edition drop — use `editionNumber` there. */
    tokenId: number | string | null;
    name: string | null;
    mintAddress?: string;
    imageUrl: string | null;
    animationUrl?: string | null;
    mediaType: string;
    previewImageUrl: string | null;
    isRevealed: boolean;
    revealAfter: string | null;
    editionNumber?: number;
    maxEditionSupply?: number;
    rarity_score?: number | null;
    rarity_rank?: number | null;
    rarity_tier?: string | null;
    pityTriggered?: boolean;
    poolName?: string;
    /** `amount` is in the token's smallest unit. */
    bounty?: {
        bountyType: string;
        amount: number | null;
        tokenSymbol: string | null;
        payoutStatus: string;
    } | null;
}

V1NftBounty ​

A bounty attached to a minted NFT. amount is in the token's smallest unit.

ts
export interface V1NftBounty {
    bountyType: string;
    amount: number | null;
    tokenSymbol: string | null;
    payoutStatus: string;
    memo?: string | null;
}

V1PeggedPrice ​

peggedPrice(). The quote expires at expiresAt; refresh it rather than reuse it.

ts
export type V1PeggedPrice = (V1PeggedPriceBase & {
    pegMode: "usd";
    usdEquivalent: number;
}) | (V1PeggedPriceBase & {
    pegMode: "native";
    nativeEquivalent: number | null;
    nativePrice: number;
});

V1PeggedPriceBase ​

Fields shared by both peg modes of peggedPrice(). Times are milliseconds since the epoch.

ts
export interface V1PeggedPriceBase {
    success: true;
    phaseId: string;
    tokenAmount: number;
    tokenSymbol: string | null;
    tokenDecimals: number | null;
    tokenPrice: number;
    usdValue: number;
    slippageTolerance: number;
    source: string;
    isStale: boolean;
    quotedAt: number;
    expiresAt: number;
    validitySeconds: number;
}

V1Phase ​

ts
export interface V1Phase {
    id: string;
    name: string | null;
    status: "active" | "upcoming" | "ended";
    startDate: string | null;
    endDate: string | null;
    /** Resolved. See the file header — never recompute this. */
    priceDisplay: PriceDisplay;
    bogo: {
        buy: number;
        get: number;
    } | null;
    isGated: boolean;
    maxPerWallet: number | null;
    maxPerTransaction: number | null;
    phaseSupply: number | null;
    /** Derived from the SERVER clock, so a skewed device clock cannot mislead. */
    msUntilStart: number | null;
    msUntilEnd: number | null;
}

V1ProfileEffect ​

A minter's cosmetic profile effect, as rendered next to their name.

ts
export interface V1ProfileEffect {
    effect_type: string | null;
    effect_category: string | null;
    config: unknown;
}

V1RecentlyMinted ​

recentlyMinted(). total is the length of this feed, not the drop's supply.

ts
export interface V1RecentlyMinted {
    success: true;
    nfts: V1RecentMint[];
    total: number;
    delayedRevealEnabled: boolean;
    editionType: string;
    allowImageDownload: boolean;
}

V1RecentMint ​

One entry in the live "just minted" feed.

ts
export interface V1RecentMint extends V1MintedItem {
    masterNftId?: string | null;
    previewImage: string | null;
    maxEditionSupply: number | null;
    isMasterEdition: boolean;
    currentEditionSupply: number | null;
    animationUrl: string | null;
    mediaType: string | null;
    isPatron: boolean;
    customTag: string | null;
    generationStatus?: string | null;
    currentPlaceholderSequence?: number | null;
    audioUrl?: string | null;
    audioPreviewUrl?: string | null;
    coverArtUrl?: string | null;
}

V1TokenPrices ​

tokenPrices(). A price of 0 means unavailable — check sources[symbol].

ts
export interface V1TokenPrices {
    success: true;
    prices: Record<string, number>;
    sources: Record<string, string>;
    /** Milliseconds since the epoch. */
    timestamp: number;
    cacheTTL: number;
}

V1TopHolders ​

topHolders(). Up to 10. error is set (with an empty list) when on-chain holder data could not be read — the request itself still succeeds, so check it before rendering "no holders".

ts
export interface V1TopHolders {
    success: true;
    topHolders: Array<V1LeaderboardEntry & {
        holdCount: number;
    }>;
    /** Distinct holders found, not the drop's supply. */
    total: number;
    error?: string;
}

V1TopMinters ​

topMinters(). Up to 10.

ts
export interface V1TopMinters {
    success: true;
    topMinters: Array<V1LeaderboardEntry & {
        mintCount: number;
    }>;
    total: number;
}

V1Trait ​

ts
export interface V1Trait {
    traitType: string;
    uniqueValues: number;
    values: V1TraitValue[];
}

V1Traits ​

traits(). Before a delayed reveal, traits is empty and message says why; totalNfts and methodology are only present once traits are public.

ts
export interface V1Traits {
    success: true;
    traits: V1Trait[];
    totalNfts?: number;
    methodology?: string;
    message?: string;
}

V1TraitValue ​

ts
export interface V1TraitValue {
    value: string;
    count: number;
    minted: number;
    available: number;
    probability: number;
    /** 0-100. */
    percentage: number;
    informationContent: number;
    rarityScore: number;
}

V1WalletMints ​

walletMints(). phaseMints is keyed by phase id. totalMinted counts every NFT this wallet minted from the drop, bonus mints included — it is not a sum of phaseMints.

ts
export interface V1WalletMints {
    success: true;
    walletAddress: string;
    collectionId: string;
    phaseMints: Record<string, V1WalletPhaseMints>;
    totalMinted: number;
}

V1WalletPhaseMints ​

One phase in walletMints().

ts
export interface V1WalletPhaseMints {
    phaseName: string | null;
    minted: number;
    /** 0 means unlimited. */
    maxPerWallet: number;
    whitelistAllocation: number | null;
    nftGateAllocation: number | null;
    tokenGateAllocation: number | null;
    allocationDetails: Array<{
        type: "whitelist";
        allocation: number;
    } | {
        type: "nft_gate";
        ruleName: string;
        multiplier: number;
        collections: unknown;
    } | {
        type: "token_gate";
        ruleName: string;
        multiplier: number;
        tokenAddress: unknown;
    }>;
    hasAllocationOverride: boolean | null;
    /** null when the phase has no per-wallet limit. */
    remaining: number | null;
    canMintMore: boolean;
    isPackPhase?: boolean;
}