GraveMint SDK — method reference
@solanadeads/gravemint · version 1.2.1 · install with npm i @solanadeads/gravemint
Every public method in this version, with its signature and types. For how to put them together, start with the GraveMint SDK guide.
import { GraveMintClient } from "@solanadeads/gravemint";
const client = new GraveMintClient({ apiKey: "gm_pub_..." });Contents
client.platform— 2 methodsclient.discovery— 7 methodsclient.collections— 3 methodsclient.nfts— 1 methodclient.bounty— 2 methodsclient.codes— 1 methodclient.v1— 19 methodsclient.mint— 4 methods- Functions — 10
- Classes — 11
- Types — 32
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.
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.
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: [...]}
getFeatured(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getLiveMints()
GET /live-mints — {success, mints: [...]}
getLiveMints(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getTrending()
GET /trending — {success, type, collections: [...]}
getTrending(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getUpcoming()
GET /upcoming-mints — {success, mints: [...]}
getUpcoming(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.getRecentSoldouts()
GET /recent-soldouts — {collections: [...]} (no success wrapper)
getRecentSoldouts(filters?: DiscoveryFilters, options?: RequestOptions): Promise<CollectionSummary[]>;discovery.listCollections()
GET /collections — {success, collections: [...]}
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.
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.
get(identifier: CollectionIdentifier, options?: RequestOptions): Promise<CollectionResponse>;collections.getGallery()
GET /collection/:identifier/gallery
Paginated list of NFTs in the collection.
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).
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.
get(identifier: NftIdentifier, options?: RequestOptions): Promise<RecentMint>;client.bounty
First-party only — not usable with a partner key. See Legacy read API.
bounty.getSummary()
getSummary(identifier: CollectionIdentifier, options?: RequestOptions): Promise<BountySummary>;bounty.getPrizes()
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, ...}.
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.
collection(identifier: string): Promise<V1Collection>;v1.eligibility()
Can this wallet mint in this phase, and for how many?
Server-evaluated by necessity: the dominant gate is an NFT holding check, which needs an on-chain lookup, and an allowlist must never be published to a client. Note the verdict deliberately EXCLUDES the time window — it answers "will this wallet qualify when the phase opens", with phase.hasStarted / hasEnded reported separately, so an upcoming phase does not read as "not eligible".
eligibility(collectionId: string, phaseId: string, walletAddress: string): Promise<V1Eligibility>;v1.gallery()
The drop's own art, paginated.
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.
gallery(identifier: string, params?: {
limit?: number;
offset?: number;
}): Promise<unknown>;v1.recentlyMinted()
The live "just minted" feed for the drop — the same one gravemint.io renders.
recentlyMinted(identifier: string, params?: {
limit?: number;
}): Promise<unknown>;v1.bounty()
Bounty summary for the drop, when it has one.
bounty(identifier: string): Promise<unknown>;v1.bountyPrizes()
The bounty's public prize table.
bountyPrizes(identifier: string): Promise<unknown>;v1.walletMints()
How many this wallet has already minted, and how many remain — per phase.
🚨 Do NOT derive this from eligibility(). The two count differently on purpose: this endpoint INCLUDES bonus mints (BOGO, bounty) while the eligibility engine excludes them, so for any wallet that took one, a figure you compute yourself will be wrong in the direction that lets them try to over-mint and get refused at prepare.
walletMints(collectionId: string, walletAddress: string): Promise<unknown>;v1.claimCodes()
Does this drop use claim codes, and of what kind?
claimCodes(collectionId: string): Promise<unknown>;v1.claimCodeBenefits()
What a specific wallet is already entitled to from codes it has redeemed.
claimCodeBenefits(collectionId: string, walletAddress: string, params?: {
phaseId?: string;
blockchain?: string;
packTierId?: string;
}): Promise<unknown>;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.
validateClaimCode(collectionId: string, code: string): Promise<{
valid: boolean;
[k: string]: unknown;
}>;v1.tokenPrices()
Platform token prices — what an SPL-priced drop is worth in USD. Global, no drop.
tokenPrices(): Promise<unknown>;v1.peggedPrice()
The resolved amount for a USD- or native-pegged phase.
⚠️ phase.price on the collection response is Dutch-resolved but NOT peg-resolved, so on a pegged phase it is a stale cached token amount. Use this, never that.
peggedPrice(phaseId: string): Promise<unknown>;v1.dutchPrice()
The live price of a dynamic Dutch phase, which moves with time.
dutchPrice(phaseId: string): Promise<unknown>;v1.availability()
How much is actually mintable right now, accounting for held reservations.
availability(collectionId: string): Promise<unknown>;v1.traits()
Trait names and values, for filtering a gallery.
traits(identifier: string): Promise<unknown>;v1.allMinted()
Every minted item in the drop.
allMinted(identifier: string): Promise<unknown>;v1.topHolders()
Largest holders — social proof for a mint page.
topHolders(identifier: string): Promise<unknown>;v1.topMinters()
Who minted the most.
topMinters(identifier: string): Promise<unknown>;v1.version()
What the API is; useful for degrading deliberately rather than on a 404.
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.
prepare(input: PrepareMintInput): Promise<PreparedMint>;mint.execute()
Hand the signed transaction back so GraveMint broadcasts it.
execute(input: {
sessionId: string;
signedTransaction: string;
}): Promise<Record<string, unknown>>;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.
executeBatch(entries: Array<{
sessionId: string;
signedTransaction: string;
}>): Promise<Record<string, unknown>>;mint.mint()
prepare → sign → execute, in one call.
Throws when the prepared response carries no transaction to sign. That is not a failure so much as a drop this SDK cannot complete in-app — an EVM gasless mint has nothing for a wallet to sign, and an XRPL drop is custodial. Check capabilities.supportedBySurface first and send the collector to mintUrl rather than discovering it here.
mint(input: PrepareMintInput & {
signer: TransactionSigner;
}): Promise<Record<string, unknown>>;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.
export declare function fromHttpStatus(status: number, message: string, body: unknown, requestId?: string): GraveMintError;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.
export declare function isUuid(s: string): boolean;looksLikeAddress
Returns true if the string looks like a valid on-chain address (any chain).
export declare function looksLikeAddress(s: string): boolean;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
idat 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).
export declare function sanitize<T = unknown>(value: T): T;validateClaimCode
Validate a claim code (format-level only, not existence).
export declare function validateClaimCode(code: unknown): string;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.
export declare function validateCollectionIdentifier(id: unknown): string;validateLimit
Validate a pagination limit against a max. Returns the clamped value.
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.
export declare function validateNftIdentifier(id: unknown): string;validateOffset
Validate an offset.
export declare function validateOffset(offset: unknown): number;validateWalletAddress
Validate a wallet address. Returns the input unchanged on success.
export declare function validateWalletAddress(addr: unknown): string;Classes
AuthError
Server returned 401 / 403 — API key missing, invalid, or origin not allowed.
export declare class AuthError extends GraveMintError {
}ConfigError
Configuration error — bad apiKey, missing baseUrl, invalid options.
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.).
export declare class GraveMintError extends Error {
readonly name: string;
readonly status?: number | undefined;
readonly code?: string | undefined;
readonly requestId?: string | undefined;
constructor(message: string, opts?: {
status?: number;
code?: string;
requestId?: string;
cause?: unknown;
});
}HttpClient
export declare class HttpClient {
private readonly baseUrl;
private readonly apiKey;
private readonly timeoutMs;
private readonly maxRetries;
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;
}NetworkError
Network failure — DNS, TCP, TLS, fetch timeout.
export declare class NetworkError extends GraveMintError {
}NotFoundError
Server returned 404 — resource does not exist.
export declare class NotFoundError extends GraveMintError {
}RateLimitError
Server returned 429 — rate limit hit. retryAfterMs is the server-suggested wait.
export declare class RateLimitError extends GraveMintError {
readonly retryAfterMs: number;
constructor(message: string, retryAfterMs: number, opts?: {
status?: number;
code?: string;
requestId?: string;
});
}ServerError
Server returned 5xx after exhausting retries.
export declare class ServerError extends GraveMintError {
}TimeoutError
Request was aborted (client-side timeout or external AbortSignal).
export declare class TimeoutError extends GraveMintError {
}TokenBucketRateLimiter
export declare class TokenBucketRateLimiter {
private tokens;
private lastRefill;
private readonly refillRate;
private readonly capacity;
private readonly queue;
constructor(config: RateLimitConfig);
/**
* Acquire one token. Resolves immediately if available, otherwise waits.
* Honors `signal` if provided — aborting the signal rejects the wait.
*/
acquire(signal?: AbortSignal): Promise<void>;
private refill;
}ValidationError
Input validation error — bad address, malformed identifier, out-of-range limit.
export declare class ValidationError extends GraveMintError {
}Types
Blockchain
Public-facing types for the GraveMint SDK.
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
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
export interface BountyPrizesResponse {
enabled: boolean;
show_occurrence?: boolean;
show_winners?: boolean;
sections?: BountyPrize[];
}BountySummary
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.
export interface CodeValidationResult {
valid: boolean;
/** Server-side reason string when valid=false. */
error?: string;
grantType?: "whitelist" | "free_mint" | "discount";
discountPercent?: number;
collectionShortId?: string | null;
collectionAddress?: string | null;
[key: string]: unknown;
}CollectionDetail
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
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
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.
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
export type CollectionResponse = CollectionDetailResponse | CollectionPreLaunchResponse;CollectionSearchResult
/collections/search returns snake_case unlike the camelCase feeds.
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
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.
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
export interface DiscoveryFilters {
/** Limit results. Capped at 100. Default 20. */
limit?: number;
/** Filter by blockchain. Server ignores unknown values. */
blockchain?: Blockchain;
}GraveMintClientOptions
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 — comfortably below the server's ~80 reads/min/IP ceiling.
*/
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
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;
}NftIdentifier
export type NftIdentifier = string;Phase
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
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
export interface PlatformSetting {
setting_key: string;
setting_value: unknown;
is_public: boolean;
}PreparedMint
The response from /v1/prepare-mint.
🚨 v1 always returns the BATCH shape — transactions[] and sessions[] — even for a quantity of 1. The first-party /gravemint/mint/prepare route rewrites single mints into a flatter legacy shape carrying a top-level sessionId, but that rewrite lives in that route's wrapper, and v1 calls prepareMintHandler directly. Read sessions[0].sessionId.
sessionId stays declared and optional so a host proxying through the legacy route still type-checks; prefer the array.
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;
}>;
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
export interface PrepareMintInput {
collectionId: string;
phaseId: string;
quantity: number;
walletAddress: string;
/** Referral attribution. Validated server-side; ignored if the drop has none. */
affiliateCode?: string | null;
}PriceDisplay
The four honest shapes GraveMint's price resolver returns.
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 }.
export interface RateLimitConfig {
/** Sustained request rate per second. */
requestsPerSecond: number;
/** Max burst — tokens accumulated when idle. */
burst: number;
}RecentMint
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
export interface RequestOptions {
/** Per-request timeout override (ms). */
timeoutMs?: 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
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.
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[]>;
}V1Capabilities
What a drop NEEDS, and whether a core-mint client can render it faithfully.
When supportedBySurface is false, show mintUrl rather than a panel that silently omits the mechanic the drop is built around. This is the protection that additive-only versioning cannot give you: a pinned client stays CORRECT as new mechanics ship, instead of quietly rendering a partial deal.
export interface V1Capabilities {
requiresFeatures: string[];
supportedBySurface: boolean;
mintUrl: string | null;
}V1Collection
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, so a schedule can be rendered honestly. */
phases: {
all: V1Phase[];
active: V1Phase[];
upcoming: V1Phase[];
};
capabilities: V1Capabilities;
/** GraveMint's clock. Render countdowns against this, never `Date.now()`. */
serverTime: string;
}V1Eligibility
export interface V1Eligibility {
gated: boolean;
meetsRequirements?: boolean;
onAllowlist?: boolean | null;
spotsAllocated?: number | null;
spotsRemaining?: number | null;
requirements?: Array<{
type: string;
met: boolean;
description: string;
}>;
phase: {
id: string;
name: string;
hasStarted: boolean;
hasEnded: boolean;
};
message?: string;
}V1Phase
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;
}