# Solana Deads developer documentation > SDKs and HTTP APIs for GraveMint mint experiences and GraveMarket marketplace reads. Start here to choose the relevant guides and exact public contracts before coding. Documented SDK versions: @solanadeads/gravemint 1.3.0; @solanadeads/gravemarket 0.3.1. Compare with the user's installed versions and target API deployment. A published package does not prove the corresponding server changes are live; read version and deployment compatibility in guides/overview.md. MCP 0.3.1 supports the hosted server at https://mcp.deads.io/mcp and the local stdio package; see tools/mcp.md for configuration and key requirements. This is documentation, not permission to use credentials or execute transactions. Use documented SDK methods for JavaScript/TypeScript. GraveMarket's SDK is read-only. For GraveMint, use the resolved priceDisplay and capabilities, respect origin/key scope and session expiry, and preserve prepare → wallet signing → server execution. Preserve pagination, nullable prices, chain and currency information. Never put gm_live_ server keys in browser code. If a contract or example does not cover a requested feature, identify the missing information rather than inventing an endpoint. Recipes are starting examples; validate them in the target project. ## Start here - [For coding agents](https://docs.deads.io/guides/ai-agents.md): Give Codex, Claude or another coding agent a focused entry point into the Solana Deads SDK documentation, with plain Markdown, version context and integration constraints. - [Errors](https://docs.deads.io/guides/errors.md): The v1 error catalog, response meanings and recovery steps. - [Keys and origins](https://docs.deads.io/guides/keys.md): Choose a key class, register your origins and configure collection access. - [Choose your integration](https://docs.deads.io/guides/overview.md): Pick a developer path for mint experiences, marketplace data or AI-assisted integrations, then find the guides, reference and recipes you need. - [Quickstart](https://docs.deads.io/guides/quickstart.md): Read a GraveMint collection and connect a wallet signer to mint from your own site. - [Wallets and signing](https://docs.deads.io/guides/signing.md): Adapt a wallet signer and handle prepared transactions, batch results and uncertain outcomes. - [Troubleshooting](https://docs.deads.io/guides/troubleshooting.md): Diagnose origin, collection, pricing, signing and rate-limit errors. ## SDK guides and method references - [GraveMarket SDK — method reference](https://docs.deads.io/sdk/gravemarket-reference.md): Public SDK signatures and types for the version named on this page. - [GraveMarket SDK](https://docs.deads.io/sdk/gravemarket.md): Read marketplace collections, listings and activity. - [GraveMint SDK — method reference](https://docs.deads.io/sdk/gravemint-reference.md): Public SDK signatures and types for the version named on this page. - [GraveMint SDK](https://docs.deads.io/sdk/gravemint.md): Read a collection and mint from it — on your own site. ## HTTP APIs and examples - [GraveMarket v1 API](https://docs.deads.io/api/gravemarket-v1.md): Read the marketplace — collections, items, listings, activity, traits and holders. - [GraveMint v1 API](https://docs.deads.io/api/gravemint-v1.md): The versioned partner API behind the GraveMint SDK — every example captured from production. ## Data models - [The marketplace model](https://docs.deads.io/concepts/marketplace-model.md): Collections, item identifiers, prices, activity and pagination across supported chains. - [The read model](https://docs.deads.io/concepts/read-model.md): Use collection responses to display phases, pricing, eligibility, supply and supported mint features. ## Integration recipes - [Collection page](https://docs.deads.io/recipes/collection-page.md): A marketplace collection page in React — grid, floor, traits and activity. - [React mint panel](https://docs.deads.io/recipes/react.md): Connect collection reads, wallet eligibility and a signer in a React component. ## MCP tooling - [MCP server](https://docs.deads.io/tools/mcp.md): Let an AI build your frontend against our APIs, using the official SDKs correctly. ## Optional - [Full documentation bundle](https://docs.deads.io/llms-full.txt): All pages in one text file; large. Prefer fetching the relevant linked pages first. --- DOCUMENT: https://docs.deads.io/api/gravemarket-v1.md # GraveMarket v1 API Base URL — `https://api.solanadeads.com/gravemarket/v1` The marketplace read API. Browse collections, read an item, follow sales activity, pull traits and holders. > **TIP: Public reads, with optional SDK authentication** These read consoles currently send requests without an `X-API-Key` header. For an integration you intend to maintain, follow the [published SDK's API-key guidance](https://docs.deads.io/sdk/gravemarket.md#api-keys): the SDK accepts a key, and its documentation recommends obtaining one because anonymous access is planned to change. GraveMint has a different access model. Read [keys and origins](https://docs.deads.io/guides/keys.md) before integrating its mint flows. Prefer the SDK — it gives you typed responses, retries and pagination helpers: ```bash npm install @solanadeads/gravemarket ``` ```ts import { GraveyardClient } from '@solanadeads/gravemarket'; const gmk = new GraveyardClient(); ``` ## Identifiers A collection is addressable three ways, and every console below accepts any of them: | form | example | |---|---| | **slug** | `solana-deads` | | **on-chain address** | `7ZYBDpPou8EehaYz6nUPExy5DU1bL3E64P5AKtikwZnh` | | internal id | `b2a30c10-0ba7-4ca8-9482-397324dc64bf` | Prefer the slug: it is stable, readable, and what the marketplace URLs use. ## Try it live Examples are available below in JavaScript, TypeScript and cURL. Every console runs against **our own collection**, `solana-deads`. ### Browse collections #### GET /collections Paginated browse. Returns a cursor — see Pagination below. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | limit | query | No | 3 | | | chain | query | No | | e.g. solana-mainnet. Omit for every chain. | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const result = await market.collections.list({ limit: 3, }); console.log(result); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionsListResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const result: CollectionsListResponse = await market.collections.list({ limit: 3, }); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections?limit=3' ``` #### GET /collections/meta The chains and categories the marketplace currently indexes — use it to build filters rather than hardcoding a list. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const meta = await market.collections.meta(); console.log(meta); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionMeta } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const meta: CollectionMeta = await market.collections.meta(); console.log(meta); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/meta' ``` ### One collection #### GET /collections/{id} Full detail: stats, floor, verification, chain and standard. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | Options: solana-deads (solana-deads (slug)); 7ZYBDpPou8EehaYz6nUPExy5DU1bL3E64P5AKtikwZnh (on-chain address) | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const result = await market.collections.get("solana-deads"); console.log(result); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionDetail } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const result: CollectionDetail = await market.collections.get("solana-deads"); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads' ``` #### GET /collections/{id}/stats Headline stats plus a daily series. Includes wash_trade_count_7d — volume figures elsewhere are not filtered for it, so read this before quoting volume. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const stats = await market.collections.stats("solana-deads"); console.log(stats); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionStatsResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const stats: CollectionStatsResponse = await market.collections.stats("solana-deads"); console.log(stats); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads/stats' ``` ### Items and listings #### GET /collections/{id}/items Items in the collection, with listing state. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | | | limit | query | No | 3 | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const items = await market.collections.items("solana-deads", { limit: 3, }); console.log(items); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionItemsResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const items: CollectionItemsResponse = await market.collections.items("solana-deads", { limit: 3, }); console.log(items); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads/items?limit=3' ``` #### GET /collections/{id}/offers Standing offers against the collection. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | | | limit | query | No | 3 | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const offers = await market.collections.offers("solana-deads", { limit: 3, }); console.log(offers); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionOffersResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const offers: CollectionOffersResponse = await market.collections.offers("solana-deads", { limit: 3, }); console.log(offers); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads/offers?limit=3' ``` ### Activity #### GET /collections/{id}/activity Sales and listings for one collection — the secondary-market context that pairs with a GraveMint drop. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | | | limit | query | No | 3 | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const activity = await market.collections.activity("solana-deads", { limit: 3, }); console.log(activity); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const activity = await market.collections.activity("solana-deads", { limit: 3, }); console.log(activity); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads/activity?limit=3' ``` #### GET /activity Global activity across the marketplace. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | limit | query | No | 3 | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const result = await market.activity.list({ limit: 3, }); console.log(result); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const result = await market.activity.list({ limit: 3, }); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/activity?limit=3' ``` ### Traits and holders #### GET /collections/{id}/traits Every trait and value, with counts — what you build a rarity filter from. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const traits = await market.collections.traits("solana-deads"); console.log(traits); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionTraitsResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const traits: CollectionTraitsResponse = await market.collections.traits("solana-deads"); console.log(traits); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads/traits' ``` #### GET /collections/{id}/holders Holder distribution, with unique_holders. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | id | path | Yes | solana-deads | | | limit | query | No | 3 | | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const holders = await market.collections.holders("solana-deads", { limit: 3, }); console.log(holders); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type CollectionHoldersResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const holders: CollectionHoldersResponse = await market.collections.holders("solana-deads", { limit: 3, }); console.log(holders); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/collections/solana-deads/holders?limit=3' ``` ### Search #### GET /search Searches collections, items, profiles and traits in one call — the response is split by kind. Authentication: these documented public read examples omit X-API-Key; see the SDK guide for optional API-key guidance. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | q | query | Yes | deads | | | limit | query | No | 3 | | | type | query | No | | collection \| item \| trait. Omit for all. | **JavaScript** ```js import { GravemarketClient } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); const result = await market.search.query({ q: "deads", limit: 3, }); console.log(result); ``` **TypeScript** ```ts import { GravemarketClient, GravemarketApiError, type SearchResponse } from "@solanadeads/gravemarket"; const market = new GravemarketClient(); try { const result: SearchResponse = await market.search.query({ q: "deads", limit: 3, }); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GravemarketApiError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ 'https://api.solanadeads.com/gravemarket/v1/search?q=deads&limit=3' ``` ## Pagination List endpoints are **cursor**-paginated and return `cursor` and `hasMore`: ```ts let cursor; do { const page = await gmk.collections.activity('solana-deads', { limit: 50, cursor }); handle(page.data); cursor = page.cursor; } while (cursor); ``` The SDK also exposes `listAll` / `activityAll` async generators that drain pages for you. For a complete export, follow the cursor until there are no more pages. Use the statistics endpoints for headline totals; the number of records loaded into a UI is not the collection total. ## Differences from GraveMint v1 | | GraveMint v1 | GraveMarket v1 | |---|---|---| | credential | `gm_pub_` / `gm_live_`, invite-only | **none** | | origin binding | required | n/a | | collection scope | key is confined to its drops | n/a — public data | | writes | prepare / execute a mint | **read-only** | | pagination | `limit` + `offset` | **cursor** | ## Related - [GraveMarket SDK](https://docs.deads.io/sdk/gravemarket.md) — the typed client, generated from the published package - [GraveMint v1 API](https://docs.deads.io/api/gravemint-v1.md) — minting on your own site --- Source: https://docs.deads.io/api/gravemarket-v1 Markdown: https://docs.deads.io/api/gravemarket-v1.md --- DOCUMENT: https://docs.deads.io/api/gravemint-v1.md # GraveMint v1 API Base URL — `https://api.solanadeads.com/gravemint/v1` Recorded responses below show the API output at capture time. Live consoles let you inspect current responses for the sample collection. ## Authentication Choose a key for the environment making the request. | | header | bounded by | use it | |---|---|---|---| | **Publishable** | `X-API-Key: gm_pub_…` | the **origin allow-list on the key** | in your web page | | **Secret** | `X-API-Key: gm_live_…` | secrecy | from your server | Use a publishable key in browser applications and a secret key on your server. Register each browser origin, including staging and previews, and keep server keys out of browser bundles. See [keys and origins](https://docs.deads.io/guides/keys.md) for sandbox keys and collection scope. ## Try it live Examples are available below in JavaScript, TypeScript and cURL. Use **Send** to inspect the request and response, including status and timing. The consoles use a sandbox key scoped to **DEAD DAWGS — ONCHAIN TEST** on **solana-devnet**. This key is restricted to registered documentation origins, the sample collection and test networks. Preparation makes a live devnet request and may reserve supply when a funded wallet is supplied. Execution is an example builder: it requires a wallet-signed transaction and cannot be sent from this page. ### Discovery #### GET / No parameters. Confirms the surface and its stability contract. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const result = await gm.v1.version(); console.log(result); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const result = await gm.v1.version(); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/' ``` ### Read a drop Address a collection by its short ID, UUID or on-chain address. Use short IDs in public links and preserve address case. #### GET /collections/{identifier} Everything needed to build a mint page, in one call. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | The identifier must resolve to a collection in the key scope. Options: 7zn5qa (7zn5qa (short id)); 7mS96x56GoeE23R8DRXYusjecFY9udEw2NTnedqJ1Ed5 (on-chain address); 2e239364-fc9b-4191-ba62-b57dbac76394 (collection UUID) | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const collection = await gm.v1.collection("7zn5qa"); console.log(collection); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1Collection } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const collection: V1Collection = await gm.v1.collection("7zn5qa"); console.log(collection); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa' ``` #### Where are the sales phases? The collection response includes `phases: { active, upcoming, all }`. Each array contains phase schedules, wallet limits and resolved `priceDisplay` values. The response also includes `serverTime` for countdowns. Use the returned phase `status`. GraveMint manages phase transitions on the server; refresh the collection response when a countdown expires to obtain the current state. ### Check eligibility Server-side verdict for one wallet against one phase. Gating criteria stay on our side — you receive the decision, never the allowlist. #### GET /collections/{collectionId}/eligibility/{phaseId}/{walletAddress} Checks wallet requirements. phase.hasStarted and phase.hasEnded report the time window separately. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | path | Yes | 7zn5qa | | | phaseId | path | Yes | 8426923e-0d64-480d-925f-90324c2dedf5 | Options: 8426923e-0d64-480d-925f-90324c2dedf5 (Public (active)) | | walletAddress | path | Yes | 11111111111111111111111111111111 | Any Solana address. This one is the System Program — an example, not a person. | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const eligibility = await gm.v1.eligibility("7zn5qa", "8426923e-0d64-480d-925f-90324c2dedf5", "11111111111111111111111111111111"); console.log(eligibility); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1Eligibility } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const eligibility: V1Eligibility = await gm.v1.eligibility("7zn5qa", "8426923e-0d64-480d-925f-90324c2dedf5", "11111111111111111111111111111111"); console.log(eligibility); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/eligibility/8426923e-0d64-480d-925f-90324c2dedf5/11111111111111111111111111111111' ``` ### The rest of the drop Everything else a mint page is built from, scoped to the key the same way. The sample key is scoped to one collection. Requests for another collection return `403 COLLECTION_NOT_IN_SCOPE`. #### GET /collections/{identifier}/gallery Available artwork, with limit and offset pagination. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | | limit | query | No | 3 | | | offset | query | No | | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const gallery = await gm.v1.gallery("7zn5qa", { limit: 3, }); console.log(gallery); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1Gallery } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const gallery: V1Gallery = await gm.v1.gallery("7zn5qa", { limit: 3, }); console.log(gallery); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/gallery?limit=3' ``` #### GET /collections/{identifier}/recently-minted The live 'just minted' feed. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | | limit | query | No | 3 | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const recentlyMinted = await gm.v1.recentlyMinted("7zn5qa", { limit: 3, }); console.log(recentlyMinted); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1RecentlyMinted } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const recentlyMinted: V1RecentlyMinted = await gm.v1.recentlyMinted("7zn5qa", { limit: 3, }); console.log(recentlyMinted); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/recently-minted?limit=3' ``` #### GET /collections/{collectionId}/wallet-mints/{walletAddress} Minted and remaining counts per phase, including bonus mints. Use this response for wallet counts; eligibility uses separate allocation rules. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | path | Yes | 7zn5qa | | | walletAddress | path | Yes | 11111111111111111111111111111111 | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const walletMints = await gm.v1.walletMints("7zn5qa", "11111111111111111111111111111111"); console.log(walletMints); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1WalletMints } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const walletMints: V1WalletMints = await gm.v1.walletMints("7zn5qa", "11111111111111111111111111111111"); console.log(walletMints); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/wallet-mints/11111111111111111111111111111111' ``` #### GET /collections/{identifier}/bounty Bounty configuration and rewards. Collections without a bounty return an empty summary. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const bounty = await gm.v1.bounty("7zn5qa"); console.log(bounty); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1BountySummary } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const bounty: V1BountySummary = await gm.v1.bounty("7zn5qa"); console.log(bounty); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/bounty' ``` #### GET /collections/{identifier}/bounty-prizes The public prize table. Returns enabled: false when there is no bounty. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const bountyPrizes = await gm.v1.bountyPrizes("7zn5qa"); console.log(bountyPrizes); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1BountyPrizes } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const bountyPrizes: V1BountyPrizes = await gm.v1.bountyPrizes("7zn5qa"); console.log(bountyPrizes); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/bounty-prizes' ``` ### Claim codes Read the collection's code configuration and the benefits a wallet has already redeemed. Validation checks a code without redeeming it or consuming a use. #### GET /collections/{collectionId}/claim-codes Does this drop use codes, and of what kind? Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const claimCodes = await gm.v1.claimCodes("7zn5qa"); console.log(claimCodes); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1ClaimCodes } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const claimCodes: V1ClaimCodes = await gm.v1.claimCodes("7zn5qa"); console.log(claimCodes); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/claim-codes' ``` #### GET /collections/{collectionId}/claim-codes/benefits/{walletAddress} What a wallet is already entitled to from codes it has redeemed. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | path | Yes | 7zn5qa | | | walletAddress | path | Yes | 11111111111111111111111111111111 | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const claimCodeBenefits = await gm.v1.claimCodeBenefits("7zn5qa", "11111111111111111111111111111111"); console.log(claimCodeBenefits); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1ClaimCodeBenefits } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const claimCodeBenefits: V1ClaimCodeBenefits = await gm.v1.claimCodeBenefits("7zn5qa", "11111111111111111111111111111111"); console.log(claimCodeBenefits); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/claim-codes/benefits/11111111111111111111111111111111' ``` #### POST /collections/{collectionId}/claim-codes/validate READ-ONLY — checking a code does not redeem it or consume a use. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | path | Yes | 7zn5qa | | | code | body | Yes | TRY-A-CODE | A code belonging to a DIFFERENT drop answers exactly like an unknown one — you cannot use this to discover codes elsewhere. | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const validateClaimCode = await gm.v1.validateClaimCode("7zn5qa", "TRY-A-CODE"); console.log(validateClaimCode); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1ClaimCodeCheck } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const validateClaimCode: V1ClaimCodeCheck = await gm.v1.validateClaimCode("7zn5qa", "TRY-A-CODE"); console.log(validateClaimCode); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s -X POST \ -H 'X-API-Key: gm_test_your_key' \ -H 'Content-Type: application/json' \ -d '{"code":"TRY-A-CODE"}' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/claim-codes/validate' ``` ### Pricing Use `priceDisplay` on the phase for the initial display. Pegged prices can be marked approximate. The helpers below refresh pegged or Dutch prices without reloading the collection. #### GET /token-prices Platform token prices — what an SPL-priced drop is worth in USD. Global: it names no collection, so it carries no drop data and needs no collection scope. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const getTokenPrices = await gm.platform.getTokenPrices(); console.log(getTokenPrices); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type TokenPricesResponse } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const getTokenPrices: TokenPricesResponse = await gm.platform.getTokenPrices(); console.log(getTokenPrices); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/token-prices' ``` #### GET /phases/{phaseId}/pegged-price The resolved amount for a USD- or native-pegged phase. Scoped through the phase's own collection — a phase id is not a way around collection scope. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | phaseId | path | Yes | 8426923e-0d64-480d-925f-90324c2dedf5 | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const peggedPrice = await gm.v1.peggedPrice("8426923e-0d64-480d-925f-90324c2dedf5"); console.log(peggedPrice); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1PeggedPrice } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const peggedPrice: V1PeggedPrice = await gm.v1.peggedPrice("8426923e-0d64-480d-925f-90324c2dedf5"); console.log(peggedPrice); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/phases/8426923e-0d64-480d-925f-90324c2dedf5/pegged-price' ``` #### GET /phases/{phaseId}/dutch-price The live price of a dynamic Dutch phase, which moves with time. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | phaseId | path | Yes | 8426923e-0d64-480d-925f-90324c2dedf5 | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const dutchPrice = await gm.v1.dutchPrice("8426923e-0d64-480d-925f-90324c2dedf5"); console.log(dutchPrice); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1DutchPrice } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const dutchPrice: V1DutchPrice = await gm.v1.dutchPrice("8426923e-0d64-480d-925f-90324c2dedf5"); console.log(dutchPrice); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/phases/8426923e-0d64-480d-925f-90324c2dedf5/dutch-price' ``` ### Supply, traits and social proof #### GET /collections/{collectionId}/availability Approximate supply counts, including burned items. available may be null for open-ended supply; pending reservations can still appear available. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const availability = await gm.v1.availability("7zn5qa"); console.log(availability); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1Availability } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const availability: V1Availability = await gm.v1.availability("7zn5qa"); console.log(availability); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/availability' ``` #### GET /collections/{identifier}/traits Trait names and values, for filtering a gallery. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const traits = await gm.v1.traits("7zn5qa"); console.log(traits); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1Traits } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const traits: V1Traits = await gm.v1.traits("7zn5qa"); console.log(traits); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/traits' ``` #### GET /collections/{identifier}/all-minted One page of minted items. Continue with limit and offset for a full history. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const allMinted = await gm.v1.allMinted("7zn5qa"); console.log(allMinted); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1AllMinted } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const allMinted: V1AllMinted = await gm.v1.allMinted("7zn5qa"); console.log(allMinted); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/all-minted' ``` #### GET /collections/{identifier}/top-holders Largest holders. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const topHolders = await gm.v1.topHolders("7zn5qa"); console.log(topHolders); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1TopHolders } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const topHolders: V1TopHolders = await gm.v1.topHolders("7zn5qa"); console.log(topHolders); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/top-holders' ``` #### GET /collections/{identifier}/top-minters Who minted the most. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | identifier | path | Yes | 7zn5qa | | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const topMinters = await gm.v1.topMinters("7zn5qa"); console.log(topMinters); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1TopMinters } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const topMinters: V1TopMinters = await gm.v1.topMinters("7zn5qa"); console.log(topMinters); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s \ -H 'X-API-Key: gm_test_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/top-minters' ``` ### What your key can reach Every route that names a collection is scoped to the collections your key was issued for. Measured against production with a key scoped to one drop, asking for another: | Request | Result | |---|---| | `GET /collections/{other}` | `403 COLLECTION_NOT_IN_SCOPE` | | `GET /collections/{other}/gallery` | `403` | | `GET /collections/{other}/recently-minted` | `403` | | `GET /collections/{other}/bounty` · `/bounty-prizes` | `403` | | `GET /collections/{other}/wallet-mints/{wallet}` | `403` | | `GET /collections/{other}/eligibility/{phase}/{wallet}` | `403` | | `POST /prepare-mint` with another drop's id | `403` | | `GET /` (version — names no collection) | `200` | The refusal is deliberately identical whether or not the drop exists, so an unauthorised caller learns only that *this key* cannot read it — never whether the collection is real. Two further bounds apply on top of scope: a **key with an origin list is refused from any other origin** (`403 ORIGIN_NOT_ALLOWED`, including a request with no `Origin` at all, which is why a browser key cannot be used server-side), and a **sandbox key is refused on any production chain** (`403 SANDBOX_KEY_ON_MAINNET`). ### Prepare a mint Preparation checks eligibility, pricing and supply, then creates transactions for signing. The default sample wallet is expected to return `INSUFFICIENT_BALANCE` before a supply reservation. A funded devnet wallet can receive transactions and sessions, reserving devnet supply for the session lifetime. #### POST /prepare-mint Returns transactions for your wallet to sign and GraveMint to submit. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | collectionId | body | Yes | 2e239364-fc9b-4191-ba62-b57dbac76394 | DEAD DAWGS — ONCHAIN TEST, our own mpl-core drop on solana-devnet. | | phaseId | body | Yes | 8426923e-0d64-480d-925f-90324c2dedf5 | Options: 8426923e-0d64-480d-925f-90324c2dedf5 (Public (active)) | | walletAddress | body | Yes | 11111111111111111111111111111111 | | | quantity | body | Yes | 1 | Options: (); (); () | | affiliate_code | body | No | | Optional. Passed straight through and validated server-side. | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const result = await gm.mint.prepare({ collectionId: "2e239364-fc9b-4191-ba62-b57dbac76394", phaseId: "8426923e-0d64-480d-925f-90324c2dedf5", walletAddress: "11111111111111111111111111111111", quantity: 1, }); console.log(result); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type PreparedMint } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const result: PreparedMint = await gm.mint.prepare({ collectionId: "2e239364-fc9b-4191-ba62-b57dbac76394", phaseId: "8426923e-0d64-480d-925f-90324c2dedf5", walletAddress: "11111111111111111111111111111111", quantity: 1, }); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s -X POST \ -H 'X-API-Key: gm_test_your_key' \ -H 'Content-Type: application/json' \ -d '{"collectionId":"2e239364-fc9b-4191-ba62-b57dbac76394","phaseId":"8426923e-0d64-480d-925f-90324c2dedf5","walletAddress":"11111111111111111111111111111111","quantity":"1"}' \ 'https://api.solanadeads.com/gravemint/v1/prepare-mint' ``` ### Execute a mint #### POST /execute-mint Send the signed transaction as base64. A transactionHash returns CLIENT_BROADCAST_NOT_ALLOWED. Authentication: sandbox key class (gm_test_your_key). These are placeholders, not working credentials. Environment: the interactive example is scoped to the documented devnet sandbox. Do not substitute a production chain for a sandbox-key mint. Execution: this example is displayed but not sent by the website. Example only. Supply a wallet-signed transaction in your application to execute a mint. | Parameter | Location | Required | Default example | Guidance | | --- | --- | --- | --- | --- | | sessionId | body | Yes | | | | signedTransaction | body | Yes | | Base64. Never a transaction hash — we broadcast, not you. | **JavaScript** ```js import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); const result = await gm.mint.execute({ sessionId: "", signedTransaction: "", }); console.log(result); ``` **TypeScript** ```ts import { GraveMintClient, GraveMintError, type V1ExecuteResult } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_test_your_key" }); try { const result: V1ExecuteResult = await gm.mint.execute({ sessionId: "", signedTransaction: "", }); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` **cURL** ```bash curl -s -X POST \ -H 'X-API-Key: gm_test_your_key' \ -H 'Content-Type: application/json' \ -d '{"sessionId":"","signedTransaction":""}' \ 'https://api.solanadeads.com/gravemint/v1/execute-mint' ``` ## Recorded responses These examples were captured from the API. Sample collection data can change; use the consoles above for current values. ### Discovery Confirms the surface and its stability contract. ```js [JavaScript] import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_your_key" }); const result = await gm.v1.version(); console.log(result); ``` ```ts [TypeScript] import { GraveMintClient, GraveMintError } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_your_key" }); try { const result = await gm.v1.version(); console.log(result); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` ```bash [cURL] curl -s \ -H 'X-API-Key: gm_pub_your_key' \ 'https://api.solanadeads.com/gravemint/v1/' ``` ```json [response · HTTP 200] { "success": true, "version": "v1", "stability": "additive-only", "docs": "https://www.npmjs.com/package/@solanadeads/gravemint" } ``` ## Reading a drop One call returns everything needed to build a mint page: the collection, every phase with its **resolved** price, live supply, and our clock for countdowns. Use the collection's short ID, UUID or on-chain address. Recorded examples can reflect an earlier API deployment; the published SDK reference describes the current identifier contract. ### GET /v1/collections/:identifier ```js [JavaScript] import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_your_key" }); const collection = await gm.v1.collection("7zn5qa"); console.log(collection); ``` ```ts [TypeScript] import { GraveMintClient, GraveMintError, type V1Collection } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_your_key" }); try { const collection: V1Collection = await gm.v1.collection("7zn5qa"); console.log(collection); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` ```bash [cURL] curl -s \ -H 'X-API-Key: gm_pub_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa' ``` ```json [response · HTTP 200] { "success": true, "collection": { "id": "2e239364-fc9b-4191-ba62-b57dbac76394", "shortId": "7zn5qa", "name": "DEAD DAWGS - ONCHAIN TEST", "symbol": "ONCHAIN1", "description": "Testing", "image": "https://gravemint-storage.s3.us-east-1.amazonaws.com/launchpad-collection-images/1774886146380/collection.png", "bannerImage": "https://gravemint-storage.s3.us-east-1.amazonaws.com/launchpad-collection-images/1774886157110/banner.png", "chain": "solana-devnet", "collectionAddress": "7mS96x56GoeE23R8DRXYusjecFY9udEw2NTnedqJ1Ed5", "contractAddress": null, "externalUrl": null, "twitter": null, "discord": null, "isVerified": false }, "stats": { "totalSupply": 888, "mintedCount": 21, "availableCount": 867, "percentMinted": 2.364864864864865 }, "phases": { "all": [ { "id": "8426923e-0d64-480d-925f-90324c2dedf5", "name": "Public", "status": "active", "startDate": "2026-03-30T16:02:00.000Z", "endDate": null, "priceDisplay": { "kind": "amount", "amount": 1, "currency": "USD", "isFree": false }, "bogo": null, "isGated": false, "maxPerWallet": 100, "maxPerTransaction": 10, "phaseSupply": null, "msUntilStart": null, "msUntilEnd": null } ], "active": [ { "id": "8426923e-0d64-480d-925f-90324c2dedf5", "name": "Public", "status": "active", "startDate": "2026-03-30T16:02:00.000Z", "endDate": null, "priceDisplay": { "kind": "amount", "amount": 1, "currency": "USD", "isFree": false }, "bogo": null, "isGated": false, "maxPerWallet": 100, "maxPerTransaction": 10, "phaseSupply": null, "msUntilStart": null, "msUntilEnd": null } ], "upcoming": [] }, "capabilities": { "requiresFeatures": [ "claim_codes", "nft_selection" ], "supportedBySurface": false, "mintUrl": "https://gravemint.io/mint/7zn5qa" }, "serverTime": "2026-08-31T12:17:45.816Z" } ``` ### Price is a shape, not a number `priceDisplay` is the resolved display price. Use `prepare` when the collector is ready to mint to obtain the final wallet-specific breakdown, including fees and benefits. | `kind` | meaning | render | |---|---|---| | `amount` | a settled figure | the number and its currency | | `range` | varies within the phase | "from X" | | `hidden` | the creator chose to hide it until eligible | do not guess — say it is hidden | | `unknown` | no display price available for this request | do not render `0` | Show “Free” only when `kind` is `amount` and `isFree` is true. Keep hidden and unknown prices distinct from zero. ## Eligibility Server-side verdict for one wallet against one phase. Gating criteria stay on our side; you receive the decision, never the allowlist. ### GET /v1/collections/:collectionId/eligibility/:phaseId/:wallet ```js [JavaScript] import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_your_key" }); const eligibility = await gm.v1.eligibility("7zn5qa", "8426923e-0d64-480d-925f-90324c2dedf5", "11111111111111111111111111111111"); console.log(eligibility); ``` ```ts [TypeScript] import { GraveMintClient, GraveMintError, type V1Eligibility } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_your_key" }); try { const eligibility: V1Eligibility = await gm.v1.eligibility("7zn5qa", "8426923e-0d64-480d-925f-90324c2dedf5", "11111111111111111111111111111111"); console.log(eligibility); } catch (err) { // `code` is stable — branch on it. `message` is a sentence you can show a user. if (err instanceof GraveMintError) console.error(err.code, err.message); else throw err; } ``` ```bash [cURL] curl -s \ -H 'X-API-Key: gm_pub_your_key' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa/eligibility/8426923e-0d64-480d-925f-90324c2dedf5/11111111111111111111111111111111' ``` ```json [response · HTTP 200] { "success": true, "gated": false, "phase": { "id": "8426923e-0d64-480d-925f-90324c2dedf5", "name": "Public", "startDate": "2026-03-30T16:02:00+00:00", "endDate": null, "hasStarted": true, "hasEnded": false }, "address": "11111111111111111111111111111111", "message": "This phase is open to everyone — no allowlist or holder requirements." } ``` ## Errors Coded, and the code is the part you branch on. The human sentence may be reworded at any time; the `code` will not. ### No credential at all No `Origin`, no key. With the SDK this arrives as a thrown `GraveMintError` whose `code` is `API_KEY_REQUIRED`. ```bash [cURL] curl -s \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa' ``` ```json [response · HTTP 401] { "success": false, "error": "This request could not be authorised.", "code": "API_KEY_REQUIRED" } ``` ### An origin we do not recognise Register the requesting origin for the key. This response is distinct from a missing credential. With the SDK this arrives as a thrown `GraveMintError` whose `code` is `ORIGIN_NOT_ALLOWED`. ```bash [cURL] curl -s \ -H 'Origin: https://partner.example' \ 'https://api.solanadeads.com/gravemint/v1/collections/7zn5qa' ``` ```json [response · HTTP 403] { "success": false, "error": "This request could not be authorised.", "code": "ORIGIN_NOT_ALLOWED" } ``` | code | status | meaning | |---|---|---| | `API_KEY_REQUIRED` | 401 | no key and no recognised origin | | `API_KEY_INVALID` | 401 | the key did not match a live record | | `ORIGIN_NOT_ALLOWED` | 403 | the origin is not on this key | | `COLLECTION_NOT_IN_SCOPE` | 403 | this key is not issued for that drop | | `AUTH_UNAVAILABLE` | 503 | we could not check — retry; never treat as denial | | `CLIENT_BROADCAST_NOT_ALLOWED` | 400 | you sent a transaction hash. See below | | `CHAIN_NOT_SUPPORTED_BY_SURFACE` | 400 | v1 mints Solana only | ## Transaction submission {#the-one-rule} `prepare-mint` returns transactions for your wallet to sign. Send the signed bytes to `execute-mint`; GraveMint validates and submits them. A client-submitted transaction hash returns `CLIENT_BROADCAST_NOT_ALLOWED`. --- Source: https://docs.deads.io/api/gravemint-v1 Markdown: https://docs.deads.io/api/gravemint-v1.md --- DOCUMENT: https://docs.deads.io/concepts/marketplace-model.md # The marketplace model GraveMarket indexes NFT collections across **nine chains** — `solana-mainnet`, `ethereum-mainnet`, `polygon-mainnet`, `base-mainnet`, `bsc-mainnet`, `xrpl-mainnet`, `cronos-mainnet`, `abstract-mainnet` and `robinhood-mainnet`. Keep the chain and currency alongside each record. Item identifiers and price units depend on the chain. ```bash npm install @solanadeads/gravemarket ``` ```ts import { GraveyardClient } from '@solanadeads/gravemarket'; const gmk = new GraveyardClient(); // public API — no key, no origin registration ``` ## Collections Addressable three ways — **slug** (`solana-deads`), **on-chain address**, or internal id. Prefer the slug: it is stable, readable, and what the marketplace URLs use. ```ts const c = await gmk.collections.get('solana-deads'); ``` Related reads hang off the same identifier: `items`, `activity`, `stats`, `traits`, `holders`, `offers`, `floor`. ## Items An item is identified by **`token_address` plus `token_id`**. Solana NFTs use a unique mint address and return `token_id: null`. EVM items use a contract address and token ID. Include the chain when combining items from several networks; contracts can use the same address on different chains. ```ts const itemKey = (item) => `${item.chain}:${item.token_address}:${item.token_id ?? ''}`; ``` ## Listings and prices `listing_price` and `listing_currency` are **nullable**, and null means **not listed** — not free, and not zero. ```ts const label = item.listing_price == null ? 'Not listed' : `${item.listing_price} ${item.listing_currency}`; ``` `listing_currency` and `floor_currency` identify the units, such as SOL, ETH or XRP. Compare or aggregate prices in the same currency. For cross-currency views, convert with an explicit exchange rate and show the conversion basis. ## Collection statistics {#stats-and-a-caveat-worth-reading} ```ts const s = await gmk.collections.stats('solana-deads'); ``` Returns headline stats plus a daily series — and **`wash_trade_count_7d`**. The stats response reports `wash_trade_count_7d` separately from volume. Label unadjusted volume accordingly. For analytics that offer adjusted volume, use the documented option rather than subtracting a trade count from a currency amount. ## Activity Sales and listings, either for one collection or globally: ```ts await gmk.collections.activity('solana-deads', { limit: 50 }); await gmk.activity.list({ limit: 50 }); ``` Use the activity endpoints for a collection feed or marketplace-wide view. The [MCP server](https://docs.deads.io/tools/mcp.md) also provides collection activity reads. ## Traits and rarity ```ts const t = await gmk.collections.traits('solana-deads'); ``` Every trait and value with counts — what a rarity filter is built from. Items carry `rarity_rank` and `rarity_score`. Rarity rank is relative to a collection and methodology. Display the collection with its rank and avoid using raw rank values to compare items across collections. ## Pagination {#pagination-—-cursors-and-why-a-partial-page-lies} Collection and activity lists use cursor pagination. Read the response type for each endpoint; some return different list fields or pagination metadata. ```ts let cursor; const all = []; do { const page = await gmk.collections.activity('solana-deads', { limit: 50, cursor }); all.push(...page.data); cursor = page.cursor; } while (cursor); ``` The SDK also exposes `listAll` / `activityAll` async generators that drain for you. For full exports, follow the cursor until there are no more pages. For headline statistics, prefer the collection stats endpoint. The number of records loaded into a UI is the loaded count, not the collection total. ## How this differs from GraveMint | | GraveMint v1 | GraveMarket v1 | |---|---|---| | purpose | **mint** a drop on your site | **read** marketplace state | | credential | invite-only, origin-bound, collection-scoped | **none** | | writes | prepare / execute | read-only | | pagination | `limit` + `offset` | **cursor** | | chains | Solana only | **nine** | ## Next - [GraveMarket v1 API](https://docs.deads.io/api/gravemarket-v1.md) — request builders and response examples - [Collection page recipe](https://docs.deads.io/recipes/collection-page.md) — a React example - [GraveMarket SDK](https://docs.deads.io/sdk/gravemarket.md) — the typed client --- Source: https://docs.deads.io/concepts/marketplace-model Markdown: https://docs.deads.io/concepts/marketplace-model.md --- DOCUMENT: https://docs.deads.io/concepts/read-model.md # The read model `gm.v1.collection(identifier)` returns the collection, supply, phase schedule and supported features in one response. Use it to build the initial mint page, then request wallet-specific eligibility and mint counts when a collector connects. ```ts const drop = await gm.v1.collection('your-drop'); // drop.collection — name, images, chain and socials // drop.stats — mintedCount, totalSupply and availableCount // drop.phases — { active, upcoming, all }, each an array // drop.capabilities — supported features and the hosted mint URL // drop.serverTime — server timestamp for countdowns ``` ## Phases Each phase includes its status, schedule, supply, wallet limits and `priceDisplay`. `phases.active` and `phases.upcoming` are arrays; `phases.all` also includes ended phases. Use the returned phase `status` to decide what to display. GraveMint manages phase transitions on the server. Refresh the collection response to keep the schedule current, and use `serverTime` with `msUntilStart` or `msUntilEnd` for countdowns. A countdown reaching zero is a reason to refresh, rather than to change the phase status locally. ## Price display {#pricing-—-a-union-never-a-number} `priceDisplay` describes what can be shown before a wallet prepares a mint. Handle each `kind` explicitly: ```ts type PriceDisplay = | { kind: 'amount'; amount: number; currency: string; isFree: boolean; approximate?: boolean } | { kind: 'range'; min: number; max: number; currency: string } | { kind: 'hidden' } | { kind: 'unknown' }; ``` | Kind | Meaning | Display | | --- | --- | --- | | `amount` | A resolved amount | Amount and currency; use `isFree` for the “Free” label | | `range` | A price within a range | “0.45–1.2 SOL” | | `hidden` | Price is withheld until the wallet qualifies | “Price revealed when you qualify” | | `unknown` | No display price is available for this request | “See on GraveMint” or “—” | ```ts function renderPrice(p: PriceDisplay) { switch (p.kind) { case 'amount': return p.isFree ? 'Free' : `${p.amount} ${p.currency}`; case 'range': return `${p.min}–${p.max} ${p.currency}`; case 'hidden': return 'Price revealed when you qualify'; case 'unknown': return 'See on GraveMint'; } } ``` Keep `hidden` and `unknown` distinct from zero. A pegged display price can be marked `approximate`. Use [`peggedPrice(phaseId)`](https://docs.deads.io/api/gravemint-v1.md#pricing) to refresh the token amount without reloading the collection. v1 phases do not expose a raw `price` field. The final price breakdown comes from `prepare`. It includes the applicable wallet benefits, fees and mint settings. Display the returned values instead of rebuilding that calculation from phase fields. Only call `prepare` when the collector is ready to mint, because it reserves supply. ## Eligibility ```ts const verdict = await gm.v1.eligibility(collectionId, phaseId, wallet); ``` GraveMint checks the phase requirements, including allowlists and NFT holdings, on the server. The response reports eligibility separately from the phase's time window, so an upcoming phase can show that a wallet qualifies before minting starts. ```ts const qualifies = !verdict.gated || verdict.meetsRequirements === true; const canMintNow = qualifies && verdict.phase.hasStarted && !verdict.phase.hasEnded; ``` `prepare` checks the current requirements again when minting begins. ## Per-wallet limits ```ts const mints = await gm.v1.walletMints(collectionId, wallet); ``` Use `walletMints` for the wallet's minted and remaining counts. It includes bonus mints, such as BOGO and bounty mints. Eligibility evaluates allocations using different counting rules, so its fields are not a substitute for the wallet-mint response. ## Supported features {#capabilities-—-degrading-honestly} Check `capabilities.supportedBySurface` before enabling the mint panel. `requiresFeatures` describes the features the collection needs. Collections that use a feature outside the core mint flow, such as a pack or generative builder, can be opened on the hosted mint page. ```tsx if (!drop.capabilities.supportedBySurface) { return drop.capabilities.mintUrl ? Mint on GraveMint :

This collection is not available through this integration.

; } ``` ## Keeping the page current {#what-this-buys-you} Refresh collection and wallet data after a mint and when a phase countdown expires. Keep the last successful response visible during refreshes, with a loading or error state where appropriate. The [React recipe](https://docs.deads.io/recipes/react.md) shows how to connect the read model to a signer. --- Source: https://docs.deads.io/concepts/read-model Markdown: https://docs.deads.io/concepts/read-model.md --- DOCUMENT: https://docs.deads.io/guides/ai-agents.md # For coding agents Give your coding agent the documentation URL and the feature you want to build. Start with a small index, then let it read the product-specific guides and reference it needs. [Start with llms.txt ↗](https://docs.deads.io/llms.txt): A compact map of the docs and the rules that matter. [Read the full context ↗](https://docs.deads.io/llms-full.txt): All guides, SDK references and API examples in one text file. ## Point your agent at the docs Copy this prompt into Codex, Claude or your preferred coding agent, then fill in the project details. The prompt includes the documentation entry point and the context the agent needs. ```text Use https://docs.deads.io/llms.txt as the documentation entry point. I want to build: [describe the feature]. My framework/runtime: [React, Next.js, Node, or other]. Product and target chain: [GraveMint or GraveMarket; chain]. Installed SDK version: [version, or ask me to confirm]. Target API environment: [base URL and deployed release, if known]. Read the relevant Markdown quickstart, SDK guide, method reference, concepts and errors before coding. Use the published SDK methods and response types for my installed version. Verify target API support before relying on a new SDK contract; package publication does not prove the server has been deployed. Read the version and deployment compatibility section in the integration guide. Cite the exact docs pages you rely on and call out any version mismatch or missing detail. Preserve chain and currency information, nullable prices, pagination and loading/empty/error states. For GraveMint, respect key/origin scope, resolved priceDisplay, capabilities, batch signing and session expiry; the wallet signs and the server broadcasts. Never put a private server key in browser code. Do not invent an endpoint or assume an unsupported trading/mint flow. Explain your integration plan, implement it in my project, and validate the SDK calls and failure states. Ask for missing access or product decisions instead of guessing. ``` If your agent cannot fetch URLs, download [the full text](https://docs.deads.io/llms-full.txt) and attach it, or provide the relevant Markdown pages directly. Fetching behavior depends on the agent and its permissions; a docs file alone does not grant it network or tool access. ## Give it the right context | Your task | Start with | Then provide | | --- | --- | --- | | Mint UI | [GraveMint quickstart](https://docs.deads.io/guides/quickstart.md) | [Read model](https://docs.deads.io/concepts/read-model.md), [signing](https://docs.deads.io/guides/signing.md), [methods](https://docs.deads.io/sdk/gravemint-reference.md), [errors](https://docs.deads.io/guides/errors.md) | | Collection or activity view | [GraveMarket SDK](https://docs.deads.io/sdk/gravemarket.md) | [Marketplace model](https://docs.deads.io/concepts/marketplace-model.md), [methods](https://docs.deads.io/sdk/gravemarket-reference.md), [collection recipe](https://docs.deads.io/recipes/collection-page.md) | | Direct HTTP integration | [GraveMint API](https://docs.deads.io/api/gravemint-v1.md) or [GraveMarket API](https://docs.deads.io/api/gravemarket-v1.md) | Request parameters, generated SDK snippets and recorded examples | | Debug an integration | [Troubleshooting](https://docs.deads.io/guides/troubleshooting.md) | The installed package version, relevant error code and a sanitized request/response | The SDK guides and method references identify their package versions. Ask the agent to compare those with your lockfile before using a newer method or parameter. A generated reference is evidence of the public contract for that version, not a promise that your installed version already supports it. Ask the agent to verify the **target API environment** as well as the installed package. A package can be published before its server changes are deployed. Read [version and deployment compatibility](https://docs.deads.io/guides/overview.md#version-and-deployment-compatibility) before relying on a new response field or behavior. The [MCP setup guide](https://docs.deads.io/tools/mcp.md#connect-remotely) covers the hosted server at `https://mcp.deads.io/mcp` and the local stdio package. ## Every guide is available as Markdown Use **View Markdown** above a documentation page, or append `.md` to its clean URL. These files are generated from the same documentation source as the website. Interactive API consoles become parameter tables and JavaScript, TypeScript and cURL examples that can be read without JavaScript. - [GraveMint method reference as Markdown](https://docs.deads.io/sdk/gravemint-reference.md) - [GraveMarket method reference as Markdown](https://docs.deads.io/sdk/gravemarket-reference.md) - [Documentation index](https://docs.deads.io/llms.txt) - [Complete documentation bundle](https://docs.deads.io/llms-full.txt) ## Add MCP when you need tools Reading documentation and connecting tools are separate steps. The [MCP server guide](https://docs.deads.io/tools/mcp.md) describes the published tools and configuration for supported clients. Follow your coding agent's MCP configuration process to connect it. MCP does not replace the SDK in the application you build. GraveMint tools require a server-side key; keep it out of browser code and prompts. See [MCP key requirements](https://docs.deads.io/tools/mcp.md#install) and [keys and origins](https://docs.deads.io/guides/keys.md). ## What a useful result should include - SDK calls and parameters that exist in the version your application installs. - Links to the documentation used, plus any unresolved assumptions. - Correct chain, currency, nullable-price and pagination behavior. - Loading, empty and failure states appropriate to your feature. - For mint flows: origin/key scope, signer integration, server submission, batching and session-expiry handling. - Validation against your project, rather than a claim that an unrun snippet is production-ready. --- Source: https://docs.deads.io/guides/ai-agents Markdown: https://docs.deads.io/guides/ai-agents.md --- DOCUMENT: https://docs.deads.io/guides/errors.md # Errors API errors provide a message and, where available, a machine-readable `code`. Use the code for application logic and the message for context. > **TIP: Handling error responses** The sentence is written for a person and may be reworded at any time. The `code` is part of the contract and will not change within v1. `AUTH_UNAVAILABLE` and `LOOKUP_FAILED` indicate a temporary lookup failure. Retry with backoff; these responses do not establish that a key or collection is invalid. ## v1 error catalog {#every-code-v1-can-return} This table is generated from the API's 22-code v1 catalog. Mint handlers and middleware can return additional codes; see [SDK error handling](https://docs.deads.io/sdk/gravemint.md#errors-and-retries) and retain a message fallback for unfamiliar responses. | code | status | retry | meaning and what to do | |---|---|---|---| | `API_KEY_EXPIRED` | 401 | no | The key is past its expiry. Request a new one. | | `API_KEY_INACTIVE` | 401 | no | The key exists but is disabled. Request a replacement or ask for its status to be reviewed. | | `API_KEY_INVALID` | 401 | no | The key did not match a live record. Check the key. Terminal — do not retry. | | `API_KEY_REQUIRED` | 401 | no | No key, and no recognised origin. Send `X-API-Key`, or ask us to register your origin. | | `AUTH_UNAVAILABLE` | 503 | **yes** | The authentication check could not complete. Retry with backoff; the key has not been rejected. | | `CHAIN_DISABLED` | 400 | no | That chain is retired or paused. Terminal. Fall back to `capabilities.mintUrl`. | | `CHAIN_NOT_SUPPORTED_BY_SURFACE` | 400 | no | v1 mints Solana only. Use the mint page for other chains. | | `CLIENT_BROADCAST_NOT_ALLOWED` | 400 | no | You sent a transaction hash — meaning you broadcast it yourself. Send `signedTransaction` as base64. GraveMint submits the transaction. | | `CODE_REQUIRED` | 400 | no | A claim-code check arrived with no code. Send `{ code }` in the body. | | `COLLECTION_CLOSED` | 409 | no | The drop is closed. Terminal for minting; the read still works. | | `COLLECTION_NOT_FOUND` | 404 | no | No live drop matches that identifier. Check the short ID, UUID or on-chain address and the key scope. | | `COLLECTION_NOT_IN_SCOPE` | 403 | no | The key was not issued for that drop. A key is scoped to specific collections. Terminal. | | `COLLECTION_REQUIRED` | 400 | no | No collection was supplied. Include `collectionId`. | | `LOOKUP_FAILED` | 503 | **yes** | We could not load the drop. Retry shortly. Not a statement about whether it exists. | | `NFTS_LOCKED` | 409 | **yes** | The items are briefly held by another mint that is still settling. Supply is temporarily reserved. Ask the collector to try again shortly. | | `NOT_ELIGIBLE` | 403 | no | This wallet does not meet the phase requirements. Show the reason from the eligibility endpoint. Terminal for this wallet/phase. | | `ORIGIN_NOT_ALLOWED` | 403 | no | This origin is not on the key. Request registration of the exact origin, including staging and previews. | | `RATE_LIMITED` | 429 | **yes** | Too many requests. Back off and honour `Retry-After`. | | `SESSION_EXPIRED` | 410 | no | The prepared session timed out before the signature came back. Call `prepare` again. Never reuse a stale session. | | `TX_MODIFIED` | 400 | no | The signed transaction does not match what we built. Check the signing adapter, then prepare a new transaction. Do not resend modified bytes. | | `UNSUPPORTED_BY_SURFACE` | 409 | no | The collection requires features outside the core mint flow. Read `capabilities.requiresFeatures` and offer `capabilities.mintUrl` when available. | | `WALLET_MISMATCH` | 400 | no | The signing wallet is not the wallet the session was prepared for. Prepare and sign with the same wallet. | ## Mint recovery {#the-mint-time-ones-worth-reading-twice} - `NFTS_LOCKED`: supply is temporarily reserved by another mint. Try again shortly. - `TX_MODIFIED`: correct the signing adapter before preparing a new transaction. - `CLIENT_BROADCAST_NOT_ALLOWED`: send signed bytes for server submission. See [signing](https://docs.deads.io/guides/signing.md). - `OUTCOME_UNKNOWN`: the SDK cannot confirm the submitted mint's outcome. Retain session IDs and check the wallet before starting another mint. This SDK error is separate from the API catalog above. For batches, inspect each returned transaction's `errorCode` and `pending` fields, even when the HTTP request succeeds. --- Source: https://docs.deads.io/guides/errors Markdown: https://docs.deads.io/guides/errors.md --- DOCUMENT: https://docs.deads.io/guides/keys.md # Keys and origins GraveMint partner keys are invite-only. Request a key with the collections you plan to integrate and the origins where your application will run. ## The three classes | Class | Prefix | Use | Restrictions | | --- | --- | --- | --- | | Publishable | `gm_pub_` | Browser applications | Registered origins and collection scope | | Secret | `gm_live_` | Servers and MCP clients | Collection scope; keep the key private | | Sandbox | `gm_test_` | Browser development on test networks | Registered origins, collection scope and test networks only | Use the prefix to identify a key's class when configuring an application or contacting support. ## Publishable keys {#a-publishable-key-is-public-by-construction} A publishable key is designed to appear in a browser bundle. The API checks its registered origins and collection scope on each request. Sandbox keys add a test-network restriction. Origin checks are browser access controls; they do not make a publishable key a secret. Keep its scope limited to the collections your integration needs. ## Server keys {#gm-live-never-reaches-a-browser} Store `gm_live_` keys in server environment variables, such as `GRAVEMINT_API_KEY`. Variables with public build prefixes, including `NEXT_PUBLIC_`, `VITE_`, `REACT_APP_` and `EXPO_PUBLIC_`, can be included in browser bundles and must not contain a server key. Use a server key for backend calls. Publishable and sandbox keys require a matching `Origin` header, which a server request does not normally send. ## Registering origins Register each origin that will make browser requests: - Production, such as `https://yourdrop.io`. - The `www` hostname as well, if it serves your application. - Staging and local development origins, including their ports. - Each preview hostname, or a fixed preview hostname used across builds. Origins match exactly. An origin consists of the scheme, host and port, so HTTP and HTTPS are separate origins. Wildcard preview domains are not supported. ## Scope A key grants access to specific collections. A request outside that scope returns `403 COLLECTION_NOT_IN_SCOPE`, whether or not the requested collection exists. A key with no assigned collections does not grant collection access. Collection scope applies to reads as well as mint operations. ## Rate limits | Operation | Limit | Scope | | --- | --- | --- | | Reads | 600/min | Per IP | | `prepare` and `execute`, combined | 60/min | Per key | | Collection `prepare` | 30/10s | Per collection, shared with gravemint.io | A single mint normally uses one prepare and one execute request. Both count toward the same per-key limit, and the collection limit also applies. Handle `429` responses using `Retry-After`; see [SDK retry behavior](https://docs.deads.io/sdk/gravemint.md#errors-and-retries) for the distinction between reads and transaction submission. ## Key rotation {#if-a-key-leaks} If a server key is exposed, request revocation and replace it. Revocation prevents further use; it does not reverse completed mints. For a planned rotation, issue the replacement, update the application, then revoke the old key after confirming the replacement works. ## Sandbox keys Use a `gm_test_` key with a collection on a test network. Sandbox keys return `SANDBOX_KEY_ON_MAINNET` on production chains; production keys return `LIVE_KEY_ON_TESTNET` on test networks. The [API reference](https://docs.deads.io/api/gravemint-v1.md) uses a sandbox key scoped to a devnet collection. --- Source: https://docs.deads.io/guides/keys Markdown: https://docs.deads.io/guides/keys.md --- DOCUMENT: https://docs.deads.io/guides/overview.md # Choose your integration Start with the experience you want to build. Each path connects a working example to the concepts, method reference and troubleshooting you will need as the integration grows. ## Pick your path | Build this | Start here | Go deeper | | --- | --- | --- | | A mint experience on your own website | [GraveMint quickstart](https://docs.deads.io/guides/quickstart.md) | [SDK guide](https://docs.deads.io/sdk/gravemint.md) · [Methods & types](https://docs.deads.io/sdk/gravemint-reference.md) | | A marketplace collection or activity view | [GraveMarket quickstart](https://docs.deads.io/sdk/gravemarket.md#quick-start) | [SDK guide](https://docs.deads.io/sdk/gravemarket.md) · [Methods & types](https://docs.deads.io/sdk/gravemarket-reference.md) | | An integration with an AI coding agent | [MCP installation](https://docs.deads.io/tools/mcp.md#install) | [Available tools](https://docs.deads.io/tools/mcp.md#tools) | | Direct HTTP integration | [GraveMint v1](https://docs.deads.io/api/gravemint-v1.md) · [GraveMarket v1](https://docs.deads.io/api/gravemarket-v1.md) | Parameters, request snippets and response examples | > **TIP: JavaScript or TypeScript?** Start with the SDK. The API consoles provide SDK calls in both languages; cURL is available when you need to inspect the HTTP request. The SDK guides document runtime requirements and configuration. ## Build a mint experience 1. **Get access.** GraveMint partner keys are invite-only. Read [key classes, origins and collection scope](https://docs.deads.io/guides/keys.md) before choosing a key for your browser or server. 2. **Read the drop.** Follow the [quickstart](https://docs.deads.io/guides/quickstart.md#read-a-drop), then understand [phases, price display and capabilities](https://docs.deads.io/concepts/read-model.md). An unknown price is not zero. 3. **Connect a signer.** Follow [wallets and signing](https://docs.deads.io/guides/signing.md). Your wallet signs; the server validates and broadcasts. Check the batch and session-expiry sections before handling more than one mint. 4. **Build the UI.** Use the [React mint panel](https://docs.deads.io/recipes/react.md) as a starting point and read its limitations. It is not a complete application. 5. **Handle failure.** Use the [error reference](https://docs.deads.io/guides/errors.md), [SDK retries](https://docs.deads.io/sdk/gravemint.md#errors-and-retries) and [troubleshooting guide](https://docs.deads.io/guides/troubleshooting.md). ## Build with marketplace data 1. **Install the client.** Follow [installation and the first request](https://docs.deads.io/sdk/gravemarket.md#install), including the SDK's current API-key guidance. 2. **Choose identifiers.** Read the [marketplace model](https://docs.deads.io/concepts/marketplace-model.md): collection identifiers, item identifiers, nullable prices and chain-specific data matter. 3. **Read the data you need.** Use [collections, items, activity and the other namespaces](https://docs.deads.io/sdk/gravemarket-reference.md). The current SDK is read-only. 4. **Build a collection view.** Start with the [collection-page recipe](https://docs.deads.io/recipes/collection-page.md). Keep pagination and not-listed states visible in your own UI. 5. **Handle limits and errors.** Check [SDK API-key and rate-limit guidance](https://docs.deads.io/sdk/gravemarket.md#api-keys), [pagination](https://docs.deads.io/sdk/gravemarket.md#pagination) and [error handling](https://docs.deads.io/sdk/gravemarket.md#error-handling). ## Know what each page gives you | Documentation | Use it when you need… | | --- | --- | | Quickstarts | A first request and the order to build in | | Concepts | Meaning of fields, units, prices, phases and pagination | | SDK guides | Installation, configuration and practical usage | | Method references | Public signatures and types for the documented package version | | API references | Paths, parameters, HTTP examples and request/response inspection | | Recipes | A larger starting example, with its limitations explained | | Errors and troubleshooting | A specific failure and what to check next | ## Version and deployment compatibility The reference pages describe the **published packages**, not the deployment state of every API environment. Check your lockfile and target API before adopting a new field or behavior. These docs cover GraveMint **1.3.0**, GraveMarket **0.3.1** and MCP **0.3.1**. Check the target environment when upgrading: package and API releases can happen separately. - Check server support for public identifiers in mint preparation, minted-history pagination, unknown-outcome handling and the updated supply fields before relying on them. - Preserve `available: null` as unknown/open-ended supply, and keep burned counts distinct in your presentation. - An uncertain mint outcome is not permission to retry. Follow the [SDK mint flow](https://docs.deads.io/sdk/gravemint.md#minting), retain session identifiers, and check the wallet before starting again. - Connect through the [hosted MCP server](https://docs.deads.io/tools/mcp.md#connect-remotely) at `https://mcp.deads.io/mcp`, or run the published package locally over stdio. Both options use the same documented tool contract. ## Before you ship - Confirm the package version shown on the SDK page matches your integration. - Test loading, empty, error and paginated states rather than only a populated response. - Preserve chain and currency information; do not display a missing price as free. - For mint flows, check origins, key scope, signing, session expiry and unsupported capabilities. - Keep private keys and server credentials out of browser bundles. See [keys and origins](https://docs.deads.io/guides/keys.md). Continue with [GraveMint](https://docs.deads.io/guides/quickstart.md), [GraveMarket](https://docs.deads.io/sdk/gravemarket.md#quick-start), or [MCP](https://docs.deads.io/tools/mcp.md). --- Source: https://docs.deads.io/guides/overview Markdown: https://docs.deads.io/guides/overview.md --- DOCUMENT: https://docs.deads.io/guides/quickstart.md # Quickstart Request a [partner key](https://docs.deads.io/guides/keys.md) scoped to your collections and application origins, then install the SDK: ```bash npm i @solanadeads/gravemint ``` The package includes TypeScript types and supports ESM and CommonJS. ## Read a drop ```ts import { GraveMintClient } from '@solanadeads/gravemint'; const gm = new GraveMintClient({ apiKey: 'gm_pub_…' }); const drop = await gm.v1.collection('deads'); // drop.collection — name, image, chain and socials // drop.stats — totalSupply, mintedCount, availableCount, percentMinted // drop.phases.active — array of active phases // drop.serverTime — server timestamp for countdowns ``` The collection read accepts a short ID, UUID or on-chain address. Prefer the short ID for public URLs and preserve address case. ## Render the price correctly Read the phase's `priceDisplay` and handle each `kind`: ```ts const phase = drop.phases.active[0]; const p = phase?.priceDisplay; const label = p?.kind === 'amount' ? (p.isFree ? 'Free' : `${p.amount} ${p.currency}`) : p?.kind === 'range' ? `${p.min}–${p.max} ${p.currency}` : p?.kind === 'hidden' ? 'Price revealed when you qualify' : 'See on GraveMint'; ``` This example selects the first active phase. If a collection has several, let the collector choose the appropriate phase. Display `hidden` and `unknown` without assigning them a zero price. ## Mint Use a [base64 signer](https://docs.deads.io/guides/signing.md#the-interface) to connect your wallet library. `mint()` coordinates preparation, signing and server submission: ```ts import { MintOutcomeUnknownError, type TransactionSigner } from '@solanadeads/gravemint'; async function mintOne(signer: TransactionSigner, walletAddress: string) { if (!drop.capabilities.supportedBySurface || !phase) { throw new Error('Open the hosted mint page for this collection.'); } try { const result = await gm.mint.mint({ collectionId: drop.collection.id, phaseId: phase.id, walletAddress, quantity: 1, signer, }); return result; } catch (error) { if (error instanceof MintOutcomeUnknownError) { // Save error.sessionIds and check the wallet before allowing another mint. // The submitted transaction may still complete. } throw error; } } ``` The wallet signs the prepared bytes; GraveMint validates and broadcasts them. Pass signed transactions to the SDK rather than broadcasting them with your wallet library. See [wallets and signing](https://docs.deads.io/guides/signing.md) for the manual flow, batch results and session handling. ## Supported features {#when-you-cannot-render-a-drop-faithfully} Check the collection's capabilities before enabling the mint flow. For collections that require features outside the core surface, offer the hosted mint page: ```tsx if (!drop.capabilities.supportedBySurface) { return drop.capabilities.mintUrl ? Mint on GraveMint :

This collection is not available through this integration.

; } ``` ## Next - [The read model](https://docs.deads.io/concepts/read-model.md): phases, prices, eligibility and wallet limits. - [Wallets and signing](https://docs.deads.io/guides/signing.md): signer adapters, submission and batch outcomes. - [React mint panel](https://docs.deads.io/recipes/react.md): a component example to adapt to your application. - [Keys and origins](https://docs.deads.io/guides/keys.md): browser and server configuration. - [Errors](https://docs.deads.io/guides/errors.md) and [troubleshooting](https://docs.deads.io/guides/troubleshooting.md): response codes and recovery steps. - [SDK reference](https://docs.deads.io/sdk/gravemint-reference.md), [v1 API](https://docs.deads.io/api/gravemint-v1.md) and [MCP server](https://docs.deads.io/tools/mcp.md). --- Source: https://docs.deads.io/guides/quickstart Markdown: https://docs.deads.io/guides/quickstart.md --- DOCUMENT: https://docs.deads.io/guides/signing.md # Wallets and signing GraveMint accepts a small signer interface, so you can use your application's existing wallet library. The SDK itself does not require a chain SDK. ## Transaction submission {#the-one-rule} Your wallet signs the transaction returned by `prepare`. Send those signed bytes to `execute`; GraveMint validates, submits and confirms the transaction. Keep submission in this flow. A `transactionHash` is rejected with `CLIENT_BROADCAST_NOT_ALLOWED`. Signed bytes that change the prepared transaction's instructions or accounts are rejected with `TX_MODIFIED`. ## The interface ```ts import type { TransactionSigner } from '@solanadeads/gravemint'; // TransactionSigner accepts and returns base64-encoded transactions. // signTransaction(base64Transaction: string): Promise // signAllTransactions?(base64Transactions: string[]): Promise ``` `signAllTransactions` is optional. When provided, the SDK can request approval for a batch together. Otherwise, it signs each transaction separately. ## Solana wallet-adapter Use this adapter inside a component or hook with a connected wallet. It supports versioned and legacy transactions. Browser builds also need a `Buffer` implementation, such as the `buffer` package. ```ts import { Buffer } from 'buffer'; import { Transaction, VersionedTransaction } from '@solana/web3.js'; import { useWallet } from '@solana/wallet-adapter-react'; import type { TransactionSigner } from '@solanadeads/gravemint'; export function useMintSigner(): TransactionSigner { const { signTransaction } = useWallet(); return { async signTransaction(base64) { if (!signTransaction) throw new Error('Connect a wallet that supports signing.'); const bytes = Buffer.from(base64, 'base64'); let transaction: Transaction | VersionedTransaction; try { transaction = VersionedTransaction.deserialize(bytes); } catch { transaction = Transaction.from(bytes); } const signed = await signTransaction(transaction); const serialized = signed instanceof Transaction ? signed.serialize({ requireAllSignatures: false }) : signed.serialize(); return Buffer.from(serialized).toString('base64'); }, }; } ``` ## Privy and embedded wallets Adapt your provider's Solana signing method to the same base64 input/output interface. Use a sign-only method; the provider must return the signed transaction for GraveMint to submit. ## Batch mints {#quantity-above-1-is-a-batch} A quantity above one uses several transactions and sessions. The SDK's `mint()` method coordinates them and returns per-transaction results. A partial batch can return without throwing, so inspect the results before reporting completion. ```ts const result = await gm.mint.mint({ collectionId, phaseId, walletAddress, quantity, signer }); if (result.results) { for (const transaction of result.results) { if (transaction.pending) { // Keep transaction.sessionId and check the wallet before retrying. } else if (!transaction.success) { // Handle transaction.errorCode and display transaction.error. } } } ``` For direct `executeBatch()` calls, `success: true` means at least one transaction succeeded. Read `results`, `successfulTransactions` and `failedTransactions` to determine the full outcome. ## The full flow For a single mint, the manual flow is: ```ts const prepared = await gm.mint.prepare({ collectionId, phaseId, walletAddress, quantity: 1 }); const transaction = prepared.transactions?.[0]?.transaction; const sessionId = prepared.sessions?.[0]?.sessionId; if (!transaction || !sessionId) throw new Error('No transaction was prepared.'); const signedTransaction = await signer.signTransaction(transaction); const result = await gm.mint.execute({ sessionId, signedTransaction }); ``` Preparation returns arrays even for a quantity of one. For larger quantities, sign each transaction and pair it with its corresponding session for `executeBatch()`, or use `mint()`: ```ts const result = await gm.mint.mint({ collectionId, phaseId, walletAddress, quantity: 1, signer }); ``` ## Sessions expire Preparation reserves supply with a short-lived lock. Call it when the collector is ready to mint, rather than to obtain a price for page display. Use the prepared response's `expiresInMs` for the signing countdown. A confirmed `SESSION_EXPIRED` response requires a new preparation. Keep expiry separate from an uncertain submission outcome: `MintOutcomeUnknownError` (`OUTCOME_UNKNOWN`) includes `sessionIds` for tracking a mint that may still complete. Retain those IDs, check the wallet and do not automatically mint again. Execute calls are not retried automatically by default. ## Chains The v1 mint flow supports Solana. `CHAIN_NOT_SUPPORTED_BY_SURFACE` identifies a collection outside that flow; `CHAIN_DISABLED` identifies a disabled chain. Use `capabilities.supportedBySurface` before preparation and offer `capabilities.mintUrl` when available. --- Source: https://docs.deads.io/guides/signing Markdown: https://docs.deads.io/guides/signing.md --- DOCUMENT: https://docs.deads.io/guides/troubleshooting.md # Troubleshooting Use the response `code` to identify the condition. Messages provide context for people; codes are intended for application logic. ## `403 ORIGIN_NOT_ALLOWED` Check that the request's origin exactly matches one registered for the key. Include staging, local ports and preview hostnames. Wildcards are not supported; a fixed preview hostname can simplify configuration. Browser keys also require the `Origin` header. For backend calls or MCP, use a server-side `gm_live_` key. See [keys and origins](https://docs.deads.io/guides/keys.md). ## `403 COLLECTION_NOT_IN_SCOPE` Confirm that the identifier belongs to a collection assigned to your key. The same response is used for unknown and inaccessible collections, so check for typing errors as well. A key without assigned collections has no collection access. ## `403 SANDBOX_KEY_ON_MAINNET` / `LIVE_KEY_ON_TESTNET` Match the key to the network. Use `gm_test_` for test-network collections and a production key for production collections. ## `404 COLLECTION_NOT_FOUND` on an id you know exists Check the short ID, UUID or on-chain address and confirm the collection is available through your key. Preserve address case. If using an older API deployment, verify its supported identifiers against the SDK version. ## The price is wrong, or shows 0 Read `priceDisplay` and handle all four `kind` values. Show “Free” only for `kind: 'amount'` with `isFree: true`. A pegged display price can be approximate. Use `peggedPrice(phaseId)` to refresh it; v1 phases do not expose a raw `price` field. See [pricing](https://docs.deads.io/concepts/read-model.md#pricing-%E2%80%94-a-union-never-a-number). ## “Mints remaining” differs between responses {#mints-remaining-is-wrong-for-some-wallets} Use `walletMints` for minted and remaining counts. It includes bonus mints; eligibility evaluates allocations with different counting rules. Refresh wallet data after a mint. ## `CLIENT_BROADCAST_NOT_ALLOWED` Send the signed transaction bytes as `signedTransaction`. GraveMint submits the transaction; `execute` does not accept a client-submitted `transactionHash`. See [wallets and signing](https://docs.deads.io/guides/signing.md). ## `TX_MODIFIED` The signed transaction differs from the prepared transaction. Check that the wallet adapter only signs the supplied bytes and does not add instructions or change accounts. Do not resubmit the same modified transaction. Correct the adapter and prepare a new session. ## `SESSION_EXPIRED`, or a mint that 409s An expired session needs a fresh `prepare`. A conflict such as `ALREADY_PROCESSING` or `ALREADY_COMPLETED` requires checking the existing session's outcome before starting another mint. Keep these cases separate: a request that is already processing may still complete. ## Batch result handling {#err-code-is-undefined-on-a-failed-mint} `mint()` can return a partial batch outcome without throwing. Inspect `results[]`, including each transaction's `errorCode` and `pending` state. With `executeBatch()`, top-level `success` means at least one transaction succeeded; it does not mean all succeeded. ```ts const result = await gm.mint.mint({ collectionId, phaseId, walletAddress, quantity, signer }); if (result.results) { for (const transaction of result.results) { if (transaction.pending) { // Retain transaction.sessionId and check the wallet before retrying. } else if (!transaction.success) { // Display transaction.error and handle transaction.errorCode. } } } ``` For `MintOutcomeUnknownError` / `OUTCOME_UNKNOWN`, retain `sessionIds` and check the wallet. Do not automatically submit a new mint while the outcome remains uncertain. ## `429`, or mints are throttled Honor `Retry-After` and review the [rate limits](https://docs.deads.io/guides/keys.md#rate-limits). Prepare and execute share a per-key limit, and prepares also use a collection limit shared with the hosted site. The SDK retries eligible read requests with backoff. Execute calls are not retried automatically by default, because the original transaction may already have been submitted. See [SDK retries](https://docs.deads.io/sdk/gravemint.md#errors-and-retries). ## `503 AUTH_UNAVAILABLE` The authentication service could not complete the key check. Retry with backoff; this response does not mean the key is invalid. `LOOKUP_FAILED` similarly indicates that a collection lookup could not complete. ## Browser CORS errors {#nothing-appears-in-your-network-tab-at-all} Inspect the preflight (`OPTIONS`) response in the network panel. If the browser rejects that response, it will not send the subsequent API request. A `204` status alone is insufficient: check that `Access-Control-Allow-Origin` matches the requesting origin and that the requested headers and method are allowed. ## Still stuck Include the error code, key prefix (`gm_pub_`, `gm_live_` or `gm_test_`), origin, collection identifier and SDK version when contacting support. Include a sanitized response where useful. Do not send the full key. --- Source: https://docs.deads.io/guides/troubleshooting Markdown: https://docs.deads.io/guides/troubleshooting.md --- DOCUMENT: https://docs.deads.io/index.md # Solana Deads developer documentation Build mint experiences with GraveMint, read marketplace data with GraveMarket, or connect your coding agent through MCP. Start with [Choose your integration](https://docs.deads.io/guides/overview.md), [For coding agents](https://docs.deads.io/guides/ai-agents.md), [GraveMint](https://docs.deads.io/sdk/gravemint.md), [GraveMarket](https://docs.deads.io/sdk/gravemarket.md), or [MCP](https://docs.deads.io/tools/mcp.md). --- Source: https://docs.deads.io/ Markdown: https://docs.deads.io/index.md --- DOCUMENT: https://docs.deads.io/recipes/collection-page.md # Collection page This example combines collection stats, an item grid and recent activity. Adapt it to your application's loading, error and refresh behavior. GraveMarket's public reads do not require a credential. > **TIP: Integration review** With the [MCP server](https://docs.deads.io/tools/mcp.md) installed, ask your agent to *"review this file for Solana Deads integration mistakes"*. ## Install ```bash npm install @solanadeads/gravemarket ``` ```ts // lib/gravemarket.ts import { GraveyardClient } from '@solanadeads/gravemarket'; export const gmk = new GraveyardClient(); ``` The public client works without a key. See [optional API keys](https://docs.deads.io/sdk/gravemarket.md#api-keys) for authenticated use. ## The page ```tsx import { useEffect, useState } from 'react'; import { gmk } from './lib/gravemarket'; // listing_price is NULLABLE and null means NOT LISTED — not free, not zero. function priceLabel(item) { return item.listing_price == null ? 'Not listed' : `${item.listing_price} ${item.listing_currency}`; } // Include the chain and token ID for EVM collections. const itemKey = (i) => `${i.chain}:${i.token_address}:${i.token_id ?? ''}`; export function CollectionPage({ slug }: { slug: string }) { const [collection, setCollection] = useState(null); const [stats, setStats] = useState(null); const [items, setItems] = useState([]); const [activity, setActivity] = useState([]); const [cursor, setCursor] = useState(undefined); const [loading, setLoading] = useState(false); useEffect(() => { gmk.collections.get(slug).then(setCollection); gmk.collections.stats(slug).then(setStats); gmk.collections.activity(slug, { limit: 10 }).then((r) => setActivity(r.data)); loadMore(true); }, [slug]); async function loadMore(reset = false) { setLoading(true); const page = await gmk.collections.items(slug, { limit: 24, ...(reset ? {} : { cursor }), }); setItems((prev) => (reset ? page.data : [...prev, ...page.data])); setCursor(page.cursor); // null/undefined when exhausted setLoading(false); } if (!collection) return

Loading…

; return (

{collection.name}

{/* Floor currency varies BY CHAIN — always render it alongside the number. */}

Floor: {collection.floor_price ?? '—'} {collection.floor_currency ?? ''} {' · '}{collection.chain}

{stats?.wash_trade_count_7d > 0 && (

Volume figures include {stats.wash_trade_count_7d} suspected wash trade(s) in the last 7 days.

)}
    {items.map((item) => (
  • {item.name {item.name ?? item.token_address.slice(0, 8)} {priceLabel(item)} {item.rarity_rank && Rank #{item.rarity_rank}}
  • ))}
{/* `cursor` present means more pages. Do not compute totals from what is loaded. */} {cursor && ( )}

Recent activity

{activity.map((e, i) => ( {e.event_type} — {e.price ?? '—'} {e.currency ?? ''} ))}
); } ``` ## Adapting the example {#what-this-deliberately-does-not-do} Add request cancellation or stale-response guards when the selected collection changes, and display request failures with a retry action. This example fetches recent activity once; poll or refresh it if your UI needs continuing updates. Use the collection record for total supply, preserve currency labels and show “Not listed” for a missing listing price. An item key must account for the chain and, on EVM, the token ID. ## Draining every page For a full activity export, follow all pages. The SDK provides async generators: ```ts const events = []; for await (const batch of gmk.collections.activityAll(slug)) events.push(...batch); ``` For holders, follow the cursor explicitly: ```ts const holders = []; let cursor; do { const page = await gmk.collections.holders(slug, { limit: 100, cursor }); holders.push(...page.holders); cursor = page.cursor; } while (cursor); ``` The holders response uses `holders` for its list field. Check each endpoint's response type when adding pagination. ## Next - [The marketplace model](https://docs.deads.io/concepts/marketplace-model.md) — identifiers, prices and pagination - [GraveMarket v1 API](https://docs.deads.io/api/gravemarket-v1.md) — request builders and response examples - [React mint panel](https://docs.deads.io/recipes/react.md) — the GraveMint side, if you also render a mint --- Source: https://docs.deads.io/recipes/collection-page Markdown: https://docs.deads.io/recipes/collection-page.md --- DOCUMENT: https://docs.deads.io/recipes/react.md # React mint panel This single-mint example reads a collection, checks the connected wallet and submits through the SDK. It uses the first active phase; add a phase selector for collections with several active phases. Your application supplies a connected wallet address and the [base64 signer](https://docs.deads.io/guides/signing.md#solana-wallet-adapter). ## Install ```bash npm install @solanadeads/gravemint ``` ## The client Create one client for the application. Use an origin-bound `gm_pub_` key in the browser, or a `gm_test_` key for test-network collections. Keep `gm_live_` server keys out of public build variables. ```ts // lib/gravemint.ts import { GraveMintClient } from '@solanadeads/gravemint'; export const gm = new GraveMintClient({ apiKey: import.meta.env.VITE_GRAVEMINT_KEY, }); ``` ## The panel ```tsx import { useEffect, useRef, useState } from 'react'; import { MintOutcomeUnknownError, type PriceDisplay, type TransactionSigner, type V1Collection, type V1Eligibility } from '@solanadeads/gravemint'; import { gm } from './lib/gravemint'; function priceLabel(price: PriceDisplay) { switch (price.kind) { case 'amount': return price.isFree ? 'Free' : `${price.approximate ? '~' : ''}${price.amount} ${price.currency}`; case 'range': return `${price.min}–${price.max} ${price.currency}`; case 'hidden': return 'Price revealed when you qualify'; case 'unknown': return 'See on GraveMint'; } } type Props = { identifier: string; walletAddress?: string; signer?: TransactionSigner; // Persist these IDs outside the component so navigation does not lose the record. onUnknownOutcome: (sessionIds: string[]) => void; }; export function MintPanel({ identifier, walletAddress, signer, onUnknownOutcome }: Props) { const [snapshot, setSnapshot] = useState<{ identifier: string; walletAddress?: string; drop: V1Collection; verdict: V1Eligibility | null; } | null>(null); const [readError, setReadError] = useState(''); const [status, setStatus] = useState(''); const [busy, setBusy] = useState(false); const [uncertain, setUncertain] = useState(false); const [refresh, setRefresh] = useState(0); const submitting = useRef(false); useEffect(() => { let cancelled = false; async function load() { try { const drop = await gm.v1.collection(identifier); const phase = drop.phases.active[0]; const verdict = phase && walletAddress && drop.capabilities.supportedBySurface ? await gm.v1.eligibility(drop.collection.id, phase.id, walletAddress) : null; if (!cancelled) { setSnapshot({ identifier, walletAddress, drop, verdict }); setReadError(''); } } catch (error) { if (!cancelled) setReadError(error instanceof Error ? error.message : 'Could not load the collection.'); } } void load(); return () => { cancelled = true; }; }, [identifier, walletAddress, refresh]); // Refresh phase state and wallet eligibility periodically. useEffect(() => { const timer = setInterval(() => setRefresh(value => value + 1), 30_000); return () => clearInterval(timer); }, []); const current = snapshot?.identifier === identifier && snapshot.walletAddress === walletAddress ? snapshot : null; const drop = current?.drop; const verdict = current?.verdict; const phase = drop?.phases.active[0]; const qualifies = !!verdict && (!verdict.gated || verdict.meetsRequirements === true); const canMint = !!(drop?.capabilities.supportedBySurface && phase && walletAddress && signer && qualifies && verdict?.phase.hasStarted && !verdict.phase.hasEnded && !readError && !busy && !uncertain); async function mint() { if (!canMint || !drop || !phase || !walletAddress || !signer || submitting.current) return; submitting.current = true; setBusy(true); setStatus('Preparing your mint. Approve the transaction in your wallet when prompted.'); try { const result = await gm.mint.mint({ collectionId: drop.collection.id, phaseId: phase.id, walletAddress, quantity: 1, signer, }); setStatus(result.success ? 'Mint complete.' : result.message ?? 'Check the mint results.'); setRefresh(value => value + 1); } catch (error) { if (error instanceof MintOutcomeUnknownError) { setUncertain(true); setStatus('The mint may still complete. Check your wallet before starting another mint.'); onUnknownOutcome(error.sessionIds); } else { setStatus(error instanceof Error ? error.message : 'Could not complete the mint.'); } } finally { submitting.current = false; setBusy(false); } } if (!drop) return

{readError || 'Loading…'}

; if (!drop.capabilities.supportedBySurface) { return drop.capabilities.mintUrl ? Mint on GraveMint :

This collection is not available through this integration.

; } return (

{drop.collection.name}

{drop.stats.mintedCount} / {drop.stats.totalSupply} minted

{phase ? priceLabel(phase.priceDisplay) : 'No active phase.'}

{!walletAddress &&

Connect a wallet to continue.

} {walletAddress && verdict && !qualifies &&

{verdict.message ?? 'This wallet does not meet the phase requirements.'}

} {readError &&

{readError}

}

{status}

); } ``` ## Adapting the panel {#what-this-deliberately-does-not-do} Persist unknown-outcome session IDs in your application and keep minting disabled until the outcome is resolved, including after a reload. The callback provides the IDs; durable storage and reconciliation are application responsibilities. For multiple phases, add a selector and recheck eligibility when it changes. For quantities above one, handle each batch result, including pending transactions. To show a final cost breakdown or a reduced prepared quantity before signing, use the [manual prepare/sign/execute flow](https://docs.deads.io/guides/signing.md#the-full-flow). Add wallet-mint counts and remaining allocations with `walletMints` if the UI displays them. Anchor countdowns to `serverTime` and refresh the collection at a transition. Preparation checks current eligibility and supply again. ## Next - [The read model](https://docs.deads.io/concepts/read-model.md): response fields and display behavior. - [Wallets and signing](https://docs.deads.io/guides/signing.md): adapters, batches and recovery. - [Troubleshooting](https://docs.deads.io/guides/troubleshooting.md): errors and next steps. --- Source: https://docs.deads.io/recipes/react Markdown: https://docs.deads.io/recipes/react.md --- DOCUMENT: https://docs.deads.io/sdk/gravemarket-reference.md # GraveMarket SDK — method reference **`@solanadeads/gravemarket`** · version **0.3.1** · install with `npm i @solanadeads/gravemarket` Signatures and types are generated from the published package. Explanatory prose is edited for clarity. For how to put them together, start with the [GraveMarket SDK guide](https://docs.deads.io/sdk/gravemarket.md). ```ts import { GravemarketClient } from "@solanadeads/gravemarket"; const client = new GravemarketClient(); ``` ## Contents - [`client.collections`](#client-collections) — 18 methods - [`client.creators`](#client-creators) — 3 methods - [`client.items`](#client-items) — 8 methods - [`client.orders`](#client-orders) — 5 methods - [`client.activity`](#client-activity) — 2 methods - [`client.search`](#client-search) — 2 methods - [`client.analytics`](#client-analytics) — 5 methods - [`client.recommendations`](#client-recommendations) — 1 method - [`client.platform`](#client-platform) — 9 methods - [`client.affiliates`](#client-affiliates) — 2 methods - [`client.config`](#client-config) — 1 method - [`client.smartMoney`](#client-smartmoney) — 3 methods - [`client.walletAnalytics`](#client-walletanalytics) — 2 methods - [`client.trust`](#client-trust) — 2 methods - [Functions](#functions) — 13 - [Classes](#classes) — 5 - [Types](#types) — 160 ## `client.collections` ### `collections.meta()` ```ts meta(): Promise; ``` ### `collections.launches()` ```ts launches(query?: LaunchesQuery): Promise; ``` ### `collections.sparklines()` ```ts sparklines(query: SparklinesQuery): Promise; ``` ### `collections.list()` ```ts list(query?: ListCollectionsQuery): Promise; ``` ### `collections.get()` ```ts get(id: string): Promise; ``` ### `collections.items()` ```ts items(id: string, query?: CollectionItemsQuery): Promise; ``` ### `collections.activity()` ```ts activity(id: string, query?: CollectionActivityQuery): Promise>; ``` ### `collections.stats()` ```ts stats(id: string, query?: CollectionStatsQuery): Promise; ``` ### `collections.traits()` ```ts traits(id: string): Promise; ``` ### `collections.holders()` ```ts holders(id: string, query?: HoldersQuery): Promise; ``` ### `collections.offers()` ```ts offers(id: string, query?: { limit?: number; }): Promise; ``` ### `collections.traitFloors()` ```ts traitFloors(id: string): Promise; ``` ### `collections.traitPricingSummary()` ```ts traitPricingSummary(id: string): Promise; ``` ### `collections.crossChain()` ```ts crossChain(id: string): Promise; ``` ### `collections.analytics()` ```ts analytics(id: string, query?: { period?: string; }): Promise; ``` ### `collections.floor()` ```ts floor(id: string): Promise; ``` ### `collections.listAll()` ```ts listAll(query?: ListCollectionsQuery): AsyncGenerator; ``` ### `collections.activityAll()` ```ts activityAll(id: string, query?: CollectionActivityQuery): AsyncGenerator; ``` ## `client.creators` Public creator pages — brand pages (e.g. /creators/buxdao) grouping multiple collections under one creator. Read-only; only published pages are visible. ### `creators.list()` Paginated list of published creator pages. ```ts list(query?: CreatorPagesQuery): Promise; ``` ### `creators.get()` A creator page (by slug) with its member collections and stats. ```ts get(slug: string): Promise; ``` ### `creators.byCollection()` The published creator page housing a collection (UUID), or null. ```ts byCollection(collectionId: string): Promise; ``` ## `client.items` ### `items.get()` Fetch an item by Solana mint address or EVM `contract:token_id`. Pass `{ chain }` when a contract exists on more than one chain — see {@link ItemChainHint}. ```ts get(id: string, opts?: ItemChainHint): Promise; ``` ### `items.activity()` ```ts activity(id: string, query?: ItemActivityQuery): Promise>>; ``` ### `items.offers()` ```ts offers(id: string, query?: ItemOffersQuery): Promise>; ``` ### `items.listings()` ```ts listings(id: string, query?: ItemListingsQuery): Promise>; ``` ### `items.traits()` ```ts traits(id: string, opts?: ItemChainHint): Promise; ``` ### `items.neighbors()` ```ts neighbors(id: string, query?: ItemNeighborsQuery): Promise; ``` ### `items.priceHistory()` ```ts priceHistory(id: string, query?: ItemPriceHistoryQuery): Promise; ``` ### `items.rarity()` Convenience wrapper for rarity rank/score/tier. Resolves to `{ rank, score, tier, total_supply }`. ```ts rarity(id: string, opts?: ItemChainHint): Promise; ``` ## `client.orders` ### `orders.aggregatedListings()` ```ts aggregatedListings(collectionId: string, query?: AggregatedListingsQuery): Promise; ``` ### `orders.bestPrice()` ```ts bestPrice(tokenAddress: string, query: { chain: string; }): Promise; ``` ### `orders.aggregatedFloor()` ```ts aggregatedFloor(collectionId: string): Promise; ``` ### `orders.offersSummary()` ```ts offersSummary(collectionId: string): Promise; ``` ### `orders.onchainStatus()` ```ts onchainStatus(assetId: string): Promise; ``` ## `client.activity` ### `activity.list()` ```ts list(query?: ActivityQuery): Promise>; ``` ### `activity.listAll()` ```ts listAll(query?: ActivityQuery): AsyncGenerator; ``` ## `client.search` ### `search.query()` ```ts query(query: SearchQuery): Promise; ``` ### `search.traits()` ```ts traits(query: TraitSearchQuery): Promise; ``` ## `client.analytics` ### `analytics.platform()` ```ts platform(query?: AnalyticsQuery): Promise; ``` ### `analytics.collection()` ```ts collection(id: string, query?: AnalyticsQuery): Promise>; ``` ### `analytics.compare()` ```ts compare(query: CompareCollectionsQuery): Promise; ``` ### `analytics.heatmap()` ```ts heatmap(query: LiquidityHeatmapQuery): Promise; ``` ### `analytics.flow()` ```ts flow(collectionId: string, query?: CollectionFlowQuery): Promise; ``` ## `client.recommendations` ### `recommendations.similar()` ```ts similar(itemId: string, query?: SimilarItemsQuery): Promise; ``` ## `client.platform` ### `platform.tokenPrices()` ```ts tokenPrices(query?: TokenPricesQuery): Promise; ``` ### `platform.price()` ```ts price(query: PriceQuery): Promise; ``` ### `platform.fees()` ```ts fees(): Promise; ``` ### `platform.fee()` ```ts fee(feeType: string): Promise; ``` ### `platform.chains()` ```ts chains(): Promise; ``` ### `platform.branding()` ```ts branding(): Promise; ``` ### `platform.analyticsConfig()` ```ts analyticsConfig(): Promise; ``` ### `platform.status()` ```ts status(): Promise; ``` ### `platform.tokenPrice()` Convenience: look up a single token's USD price by symbol. ```ts tokenPrice(symbol: string): Promise; ``` ## `client.affiliates` ### `affiliates.config()` ```ts config(): Promise; ``` ### `affiliates.leaderboard()` ```ts leaderboard(query?: AffiliateLeaderboardQuery): Promise; ``` ## `client.config` ### `config.client()` ```ts client(): Promise; ``` ## `client.smartMoney` ### `smartMoney.feed()` ```ts feed(query?: SmartMoneyFeedQuery): Promise; ``` ### `smartMoney.leaderboard()` ```ts leaderboard(query?: SmartMoneyLeaderboardQuery): Promise; ``` ### `smartMoney.inflows()` ```ts inflows(query?: SmartMoneyInflowsQuery): Promise; ``` ## `client.walletAnalytics` ### `walletAnalytics.pnl()` ```ts pnl(address: string, query?: WalletPnLQuery): Promise; ``` ### `walletAnalytics.pnlLots()` ```ts pnlLots(address: string, query?: WalletPnLLotsQuery): Promise; ``` ## `client.trust` ### `trust.collections()` ```ts collections(query?: TrustCollectionsQuery): Promise; ``` ### `trust.wallet()` ```ts wallet(address: string): Promise; ``` ## Functions ### `createHttp` ```ts export declare function createHttp(opts: HttpOptions): Http; ``` ### `isUuid` ```ts export declare function isUuid(s: string): boolean; ``` ### `looksLikeAddress` ```ts export declare function looksLikeAddress(s: string): boolean; ``` ### `paginate` ```ts export declare function paginate(fetchPage: (cursor: string | null) => Promise>): AsyncGenerator; ``` ### `sanitizeResponse` ```ts export declare function sanitizeResponse(input: T): T; ``` ### `validateAffiliateCode` Affiliate code — alphanumeric/_/-. ```ts export declare function validateAffiliateCode(code: unknown): string; ``` ### `validateChain` Chain slug like "solana-mainnet". ```ts export declare function validateChain(chain: unknown): string; ``` ### `validateCollectionIdentifier` Collection identifier: slug or on-chain contract address. ```ts export declare function validateCollectionIdentifier(id: unknown): string; ``` ### `validateFeeType` Fee-type slug like "marketplace_v1". ```ts export declare function validateFeeType(feeType: unknown): string; ``` ### `validateItemIdentifier` Item identifier: Solana mint, EVM contract:tokenId, or numeric token id. ```ts export declare function validateItemIdentifier(id: unknown): string; ``` ### `validateLimit` Clamp a numeric limit to [1, max]. ```ts export declare function validateLimit(limit: unknown, max: number, fallback: number): number; ``` ### `validateTokenSymbol` Token-symbol shape: 1-20 alphanumerics. ```ts export declare function validateTokenSymbol(symbol: unknown): string; ``` ### `validateWalletAddress` Wallet address (Solana / EVM / Sui). ```ts export declare function validateWalletAddress(addr: unknown): string; ``` ## Classes ### `GraveyardApiError` ```ts export declare class GraveyardApiError extends Error { readonly code: string; readonly status: number; readonly details?: unknown; constructor(code: string, message: string, status: number, details?: unknown); } ``` ### `GraveyardNetworkError` ```ts export declare class GraveyardNetworkError extends GraveyardApiError { constructor(message: string, cause?: unknown); } ``` ### `GraveyardRateLimitError` Thrown when the API responds with HTTP 429. Exposes `retryAfterMs` if the server set a `Retry-After` header. Falls back to `limit` / `remaining` from `X-RateLimit-*` headers when available. ```ts export declare class GraveyardRateLimitError extends GraveyardApiError { readonly retryAfterMs?: number; readonly limit?: number; readonly remaining?: number; constructor(message: string, opts?: { retryAfterMs?: number; limit?: number; remaining?: number; }); } ``` ### `GraveyardTimeoutError` ```ts export declare class GraveyardTimeoutError extends GraveyardApiError { readonly timeoutMs: number; constructor(timeoutMs: number); } ``` ### `GraveyardValidationError` ```ts export declare class GraveyardValidationError extends GraveyardApiError { constructor(message: string); } ``` ## Types ### `ActivityEvent` ```ts export interface ActivityEvent { chain: Chain; event_type: EventType; from_address: string | null; to_address: string | null; price: number | null; currency: string | null; tx_hash: string | null; event_time: string; marketplace?: string | null; market_collections?: { name: string | null; slug: string | null; image_url: string | null; custom_image_url: string | null; } | null; market_items?: { name: string | null; image_url: string | null; thumbnail_url: string | null; listing_price: number | null; listing_currency: string | null; } | null; } ``` ### `ActivityQuery` ```ts export interface ActivityQuery extends CursorPaginationQuery { chain?: Chain; type?: EventType | string; wallet?: string; mint?: string; } ``` ### `AffiliateConfigResponse` ```ts export interface AffiliateConfigResponse { is_enabled: boolean; commission_percent: number; commission_bps: number; show_leaderboard: boolean; leaderboard_size: number; } ``` ### `AffiliateLeaderboardEntry` ```ts export interface AffiliateLeaderboardEntry { affiliate_code: string; total_referred_volume: number; total_referred_sales: number; total_earned_usd: number; } ``` ### `AffiliateLeaderboardQuery` ```ts export interface AffiliateLeaderboardQuery { limit?: number; } ``` ### `AffiliateLeaderboardResponse` ```ts export interface AffiliateLeaderboardResponse { leaderboard: AffiliateLeaderboardEntry[]; } ``` ### `AggregatedFloorBest` ```ts export interface AggregatedFloorBest { price: number; currency: string; marketplace: string; token_address: string; seller: string; source: 'live_api' | 'database'; } ``` ### `AggregatedFloorResponse` ```ts export interface AggregatedFloorResponse { best: AggregatedFloorBest | null; external_tracking_enabled: boolean; } ``` ### `AggregatedListingsQuery` ```ts export interface AggregatedListingsQuery { limit?: number; offset?: number; sort?: 'price_asc' | 'price_desc' | 'newest'; marketplace?: string; } ``` ### `AggregatedListingsResponse` ```ts export interface AggregatedListingsResponse { listings: AggregatedListingsRow[]; total: number; marketplace_counts: Record; offset: number; limit: number; sort: 'price_asc' | 'price_desc' | 'newest' | string; } ``` ### `AggregatedListingsRow` ```ts export interface AggregatedListingsRow { chain: Chain; maker: string; token_address: string; token_id: string; price: number; currency: string; price_usd: number | null; expiration_at: string | null; protocol: string; source_marketplace: string; created_at: string; updated_at: string; market_items: { name: string | null; image_url: string | null; thumbnail_url: string | null; } | null; } ``` ### `AmbiguousItemCandidate` One of several items a `contract:token_id` names, from `GraveyardApiError.details.candidates` on a 409 `AMBIGUOUS_ITEM` — see {@link ItemChainHint}. Retry with `{ chain }`. (The API also sends each item's internal id; the SDK strips internal UUIDs, as everywhere.) ```ts export interface AmbiguousItemCandidate { chain: string; } ``` ### `AnalyticsConfigResponse` ```ts export interface AnalyticsConfigResponse { analytics_enabled: boolean; } ``` ### `AnalyticsQuery` ```ts export interface AnalyticsQuery { period?: '24h' | '7d' | '30d' | '90d' | '1y' | 'all'; /** Reported (raw) vs Adjusted (wash-filtered) volume mode. Default: 'adjusted'. */ volume?: 'reported' | 'adjusted'; } ``` ### `ApiEnvelope` ```ts export type ApiEnvelope = ApiResponse | ApiErrorBody; ``` ### `ApiErrorBody` ```ts export interface ApiErrorBody { success: false; error: { code: string; message: string; details?: unknown; }; } ``` ### `ApiResponse` ```ts export interface ApiResponse { success: true; data: T; } ``` ### `BestPriceListing` ```ts export interface BestPriceListing { price: number; currency: string; source_marketplace: string; maker: string; protocol: string; protocol_data: unknown | null; expiration_at: string | null; } ``` ### `BestPriceResponse` ```ts export interface BestPriceResponse { best: BestPriceListing | null; all_listings: BestPriceListing[]; count: number; token_address?: string; chain?: Chain; } ``` ### `BrandingResponse` ```ts export interface BrandingResponse { branding: Record; } ``` ### `Chain` ```ts export type Chain = string; ``` ### `ChainInfo` ```ts export interface ChainInfo { id: string; name: string; shortName: string; family: string; chainId: number | string; nativeCurrency: string; nativeCurrencyDecimals: number; coingeckoId: string; explorerUrl: string; explorerTxUrl: string; explorerAddressUrl: string; logo: string | null; color: string | null; } ``` ### `ChainsResponse` ```ts export interface ChainsResponse { chains: ChainInfo[]; } ``` ### `ClientConfigResponse` ```ts export interface ClientConfigResponse { ios_external_marketplace_enabled: boolean; ios_external_marketplace_min_app_version: string | null; ios_external_marketplace_flows: string[] | null; } ``` ### `CollectionActivityQuery` ```ts export interface CollectionActivityQuery extends CursorPaginationQuery { type?: string; wallet?: string; mint?: string; } ``` ### `CollectionAnalyticsDaily` ```ts export interface CollectionAnalyticsDaily { date: string; sales: number; volume: number; avg_price: number | null; floor: number | null; listed_count: number | null; holder_count: number | null; new_listings: number | null; unique_sellers: number | null; unique_buyers: number | null; [key: string]: unknown; } ``` ### `CollectionAnalyticsResponse` ```ts export interface CollectionAnalyticsResponse { stats: CollectionAnalyticsStats | null; daily: CollectionAnalyticsDaily[]; holder_history: HolderHistoryRow[]; wash_trade_count_7d: number; period: string; period_stats: { avg_price: number | null; unique_buyers: number; unique_sellers: number; }; deltas: { floor: number | null; volume: number | null; sales: number | null; listed: number | null; holders: number | null; top_bid: number | null; market_cap: number | null; } | null; } ``` ### `CollectionAnalyticsStats` ```ts export interface CollectionAnalyticsStats { floor_price: number | null; floor_currency: string | null; volume_24h: number | null; volume_7d: number | null; volume_30d: number | null; sales_24h: number | null; sales_7d: number | null; sales_30d: number | null; top_bid_24h: number | null; top_bid_7d: number | null; top_bid_30d: number | null; owner_count: number | null; listed_count: number | null; market_cap: number | null; updated_at: string; created_at: string; [key: string]: unknown; } ``` ### `CollectionCrossChainResponse` ```ts export interface CollectionCrossChainResponse { mappings: CrossChainMapping[]; collections: CrossChainCollection[]; } ``` ### `CollectionDetail` ```ts export interface CollectionDetail extends CollectionSummary { description: string | null; banner_url: string | null; royalty_bps: number | null; royalty_recipients: Array<{ address: string; share: number; }> | null; categories: string[] | null; created_at: string; market_collection_stats: MarketCollectionStatsRow | null; } ``` ### `CollectionFlowParty` ```ts export interface CollectionFlowParty { address: string; volume_usd: number; } ``` ### `CollectionFlowQuery` ```ts export interface CollectionFlowQuery { period?: '7d' | '30d'; } ``` ### `CollectionFlowResponse` ```ts export interface CollectionFlowResponse { collection_id: string; period: string; net_flow_usd: number; total_buy_volume_usd: number; total_sell_volume_usd: number; buyer_count: number; seller_count: number; new_buyer_count: number; returning_buyer_count: number; hold_time_histogram: Record; top_buyers: CollectionFlowParty[]; top_sellers: CollectionFlowParty[]; } ``` ### `CollectionHolder` Top holders for a collection — wallet addresses only, no profile join. ```ts export interface CollectionHolder { address: string; count: number; } ``` ### `CollectionHoldersResponse` ```ts export interface CollectionHoldersResponse { holders: CollectionHolder[]; unique_holders: number; cursor: string | null; hasMore: boolean; } ``` ### `CollectionItemsQuery` ```ts export interface CollectionItemsQuery extends CursorPaginationQuery { sort?: 'price_asc' | 'price_desc' | 'rarity_asc' | 'rarity_desc' | 'token_id_asc' | 'token_id_desc' | 'recently_listed'; listed?: boolean; min_price?: number; max_price?: number; marketplace?: string; owner?: string; traits?: string; rarity_tier?: string; with_offers?: boolean; search?: string; } ``` ### `CollectionItemsResponse` ```ts export interface CollectionItemsResponse extends CursorPaginatedResponse { marketplace_counts: Record; total?: number; } ``` ### `CollectionLaunch` ```ts export interface CollectionLaunch { name: string; slug: string; chain: Chain; contract_address: string | null; image_url: string | null; custom_image_url: string | null; banner_url: string | null; is_verified: boolean; total_supply: number | null; description: string | null; created_at: string; source: string | null; floor_price: number | null; floor_currency: string | null; volume_24h: number | null; volume_7d: number | null; volume_30d: number | null; listed_count: number | null; owner_count: number | null; sales_24h: number | null; sales_7d: number | null; sales_30d: number | null; is_gravemint: boolean; } ``` ### `CollectionLaunchesResponse` ```ts export interface CollectionLaunchesResponse { data: CollectionLaunch[]; total: number; limit: number; offset: number; hasMore: boolean; } ``` ### `CollectionMeta` ```ts export interface CollectionMeta { chains: Array<{ chain: Chain; count: number; }>; categories: string[]; } ``` ### `CollectionOfferRow` ```ts export interface CollectionOfferRow { price: number; currency: string; floor_diff_pct: number | null; fillable_offers: number; total_size: number; bidders: number; marketplace: string; } ``` ### `CollectionOffersResponse` GET /collections/:id/offers returns `{ data }` only. ```ts export interface CollectionOffersResponse { data: MarketOrder[]; } ``` ### `CollectionsListResponse` ```ts export interface CollectionsListResponse extends CursorPaginatedResponse { timeframe?: string; } ``` ### `CollectionSparklines` ```ts export type CollectionSparklines = Record; ``` ### `CollectionStatsDailyRow` ```ts export interface CollectionStatsDailyRow { date: string; sales: number; volume: number; avg_price: number | null; floor: number | null; listed_count: number | null; holder_count: number | null; new_listings: number | null; unique_sellers: number | null; unique_buyers: number | null; [extra: string]: unknown; } ``` ### `CollectionStatsQuery` ```ts export interface CollectionStatsQuery { period?: '24h' | '7d' | '30d' | '90d' | '1y' | 'all'; } ``` ### `CollectionStatsResponse` ```ts export interface CollectionStatsResponse { stats: MarketCollectionStatsRow | null; daily: CollectionStatsDailyRow[]; wash_trade_count_7d: number; } ``` ### `CollectionSummary` Collection row. Reference a collection by `slug` or `contract_address`. ```ts export interface CollectionSummary { name: string; image_url: string | null; chain: Chain; contract_address: string; slug?: string; is_verified: boolean; floor_price: number | null; total_volume: number | null; total_listed: number | null; total_supply: number | null; [extra: string]: unknown; } ``` ### `CollectionTrait` ```ts export interface CollectionTrait { trait_type: string; count: number; values: CollectionTraitValue[]; } ``` ### `CollectionTraitFloor` ```ts export interface CollectionTraitFloor { trait_type: string; trait_value: string; floor_price: number; currency: string; listed_count: number; } ``` ### `CollectionTraitFloorsResponse` ```ts export interface CollectionTraitFloorsResponse { trait_floors: CollectionTraitFloor[]; } ``` ### `CollectionTraitPricingSummary` ```ts export interface CollectionTraitPricingSummary { generated_at: string; per_trait: TraitPricingRow[]; marketplace_has_data: boolean; } ``` ### `CollectionTraitsResponse` ```ts export interface CollectionTraitsResponse { traits: CollectionTrait[]; total_items: number; } ``` ### `CollectionTraitValue` ```ts export interface CollectionTraitValue { value: string; count: number; percentage: number; } ``` ### `CompareCollectionEntry` ```ts export interface CompareCollectionEntry { id: string; meta: { id: string; name: string; slug: string; chain: Chain; image_url: string | null; is_verified: boolean; contract_address: string | null; } | null; stats: Record | null; daily: CompareDailyRow[]; holders: CompareHolderRow[]; } ``` ### `CompareCollectionsQuery` ```ts export interface CompareCollectionsQuery { collection_ids: string; period?: '24h' | '7d' | '30d' | '90d'; } ``` ### `CompareCollectionsResponse` ```ts export interface CompareCollectionsResponse { collections: CompareCollectionEntry[]; period: string; } ``` ### `CompareDailyRow` ```ts export interface CompareDailyRow { collection_id: string; date: string; volume: number; volume_usd: number | null; sales: number; avg_price: number | null; floor_close: number | null; } ``` ### `CompareHolderRow` ```ts export interface CompareHolderRow { collection_id: string; date: string; unique_holders: number | null; top_10_pct_concentration: number | null; } ``` ### `CreatorPageByCollectionResponse` ```ts export interface CreatorPageByCollectionResponse { /** Null when the collection is not on any published creator page. */ page: CreatorPageRef | null; } ``` ### `CreatorPageCollection` ```ts export interface CreatorPageCollection { id: string; slug: string | null; name: string; chain: Chain; contract_address: string | null; image_url: string | null; custom_image_url: string | null; banner_url: string | null; is_verified: boolean; total_supply: number | null; description: string | null; /** Display order on the creator page (0-based). */ position: number; added_by: 'admin' | 'owner'; market_collection_stats: CreatorPageCollectionStats | CreatorPageCollectionStats[] | null; } ``` ### `CreatorPageCollectionStats` ```ts export interface CreatorPageCollectionStats { floor_price: number | null; floor_currency: string | null; volume_24h: number | null; total_volume_all_time: number | null; listed_count: number | null; owner_count: number | null; sales_24h: number | null; total_items: number | null; } ``` ### `CreatorPageDetail` ```ts export interface CreatorPageDetail { id: string; slug: string; name: string; bio: string | null; logo_url: string | null; banner_url: string | null; website_url: string | null; twitter_url: string | null; discord_url: string | null; is_published: boolean; created_at: string; updated_at: string; } ``` ### `CreatorPageRef` ```ts export interface CreatorPageRef { slug: string; name: string; logo_url: string | null; } ``` ### `CreatorPageResponse` ```ts export interface CreatorPageResponse { page: CreatorPageDetail; collections: CreatorPageCollection[]; } ``` ### `CreatorPagesQuery` ```ts export interface CreatorPagesQuery { /** Page size — default 24, max 100. */ limit?: number; offset?: number; } ``` ### `CreatorPagesResponse` ```ts export interface CreatorPagesResponse { items: CreatorPageSummary[]; /** Offset for the next page, or null when this is the last page. */ nextOffset: number | null; } ``` ### `CreatorPageSummary` ```ts export interface CreatorPageSummary { id: string; slug: string; name: string; bio: string | null; logo_url: string | null; banner_url: string | null; created_at: string; collection_count: number; } ``` ### `CrossChainCollection` ```ts export interface CrossChainCollection { name: string; slug: string; chain: Chain; contract_address: string | null; image_url: string | null; is_verified: boolean; total_supply: number | null; floor_price: number | null; floor_currency: string | null; total_volume: number | null; mapping: CrossChainMapping; } ``` ### `CrossChainMapping` ```ts export interface CrossChainMapping { mapping_type: string | null; confidence: number | null; verified: boolean; } ``` ### `CursorPaginatedResponse` Cursor-paginated envelope used by activity feeds, listings, etc. ```ts export interface CursorPaginatedResponse { data: T[]; cursor: string | null; hasMore: boolean; total?: number; } ``` ### `CursorPaginationQuery` ```ts export interface CursorPaginationQuery { limit?: number; cursor?: string | null; } ``` ### `EventType` ```ts export type EventType = 'sale' | 'list' | 'listing' | 'delist' | 'offer' | 'transfer' | 'mint' | 'burn'; ``` ### `Fee` ```ts export interface Fee { fee_type: string; fee_bps: number; description: string; } ``` ### `FeesResponse` ```ts export interface FeesResponse { fees: Fee[]; } ``` ### `GraveyardClientOptions` ```ts export interface GraveyardClientOptions extends HttpOptions { } ``` ### `HeatmapCell` ```ts export interface HeatmapCell { dow: number; hour: number; value: number; } ``` ### `HolderHistoryRow` ```ts export interface HolderHistoryRow { date: string; unique_holders: number | null; top_10_pct_concentration: number | null; holder_distribution: unknown | null; } ``` ### `HoldersQuery` ```ts export interface HoldersQuery extends CursorPaginationQuery { } ``` ### `Http` ```ts export type Http = (path: string, init?: RequestInit) => Promise; ``` ### `HttpOptions` ```ts export interface HttpOptions { /** Defaults to `https://api.deads.io` (`https://api.solanadeads.com` serves the same API). */ baseUrl?: string; /** * API key for higher rate limits and per-key usage tracking. If omitted, * requests fall through to the anonymous (per-IP) rate limit tier. */ apiKey?: string; timeoutMs?: number; fetch?: typeof fetch; headers?: Record; /** @internal Trusted server-side use only. */ disableSanitize?: boolean; } ``` ### `ItemActivityQuery` ```ts export interface ItemActivityQuery extends ItemChainHint { limit?: number; } ``` ### `ItemActivityResponse` ```ts export interface ItemActivityResponse> { data: T[]; hasMore: boolean; } ``` ### `ItemChainHint` Which chain an item identifier refers to. A CREATE2 contract can sit at the same address on several chains, so `contract:token_id` may name more than one NFT; without a hint the API then answers 409 `AMBIGUOUS_ITEM` with `details.candidates: [{ chain }]` rather than guessing. Retry with the chain you mean (e.g. `'robinhood-mainnet'`) — choose it deliberately; taking the first candidate would just reintroduce the guess. ```ts export interface ItemChainHint { chain?: string; } ``` ### `ItemListingsQuery` ```ts export interface ItemListingsQuery extends CursorPaginationQuery, ItemChainHint { } ``` ### `ItemNeighbor` ```ts export interface ItemNeighbor { name: string | null; image_url: string | null; thumbnail_url: string | null; media_type: MediaType | string | null; rarity_rank: number | null; rarity_tier: string | null; token_id: string; token_address: string; chain: Chain; listing_price: number | null; listing_currency: string | null; } ``` ### `ItemNeighborsQuery` ```ts export interface ItemNeighborsQuery extends ItemChainHint { limit?: number; sort?: 'rank' | 'token_id' | 'price_asc' | 'price_desc'; listed?: boolean; } ``` ### `ItemNeighborsResponse` ```ts export interface ItemNeighborsResponse { current_index: number | null; total: number; sort: string; listed: boolean; before: ItemNeighbor[]; current: { rarity_rank: number | null; token_id: string; listing_price: number | null; }; after: ItemNeighbor[]; } ``` ### `ItemOffersQuery` ```ts export interface ItemOffersQuery extends CursorPaginationQuery, ItemChainHint { } ``` ### `ItemOrderListResponse` ```ts export interface ItemOrderListResponse { data: T[]; } ``` ### `ItemPriceHistoryQuery` ```ts export interface ItemPriceHistoryQuery extends ItemChainHint { period?: '24h' | '7d' | '30d' | '90d' | '1y' | 'all'; } ``` ### `ItemRarity` ```ts export interface ItemRarity { rank: number | null; score: number | null; tier: string | null; total_supply: number; } ``` ### `ItemTrait` ```ts export interface ItemTrait { trait_type: string; value: string; percentage: number | null; floor_price: number | null; floor_currency: string | null; total_count: number; listed_count: number; } ``` ### `ItemTraitsResponse` ```ts export interface ItemTraitsResponse { traits: ItemTrait[]; rarity_rank: number | null; rarity_score: number | null; rarity_tier: string | null; total_supply: number; } ``` ### `LaunchesQuery` ```ts export interface LaunchesQuery { period?: '24h' | '7d' | '30d'; chain?: Chain; limit?: number; offset?: number; } ``` ### `LiquidityHeatmapQuery` ```ts export interface LiquidityHeatmapQuery { chain: Chain; metric?: 'volume' | 'sales'; days?: 30 | 90; } ``` ### `LiquidityHeatmapResponse` ```ts export interface LiquidityHeatmapResponse { /** Active metric (volume | sales) as a 7×24 grid — drives the color gradient. */ matrix: number[][]; /** All three metrics at once so click-detail panels can render every stat * for a slot without a second round-trip. */ matrices: { volume: number[][]; sales: number[][]; avg_price: number[][]; }; chain: Chain; metric: 'volume' | 'sales'; days: 30 | 90; best: HeatmapCell; worst: HeatmapCell; } ``` ### `ListCollectionsQuery` ```ts export interface ListCollectionsQuery extends CursorPaginationQuery { sort?: 'volume_24h' | 'volume_7d' | 'volume_30d' | 'floor_price' | 'created_at' | 'name'; chain?: Chain; verified?: boolean; category?: string; timeframe?: '24h' | '7d' | '30d' | 'all'; search?: string; } ``` ### `MarketCollectionStatsRow` ```ts export interface MarketCollectionStatsRow { floor_price: number | null; floor_currency: string | null; volume_24h: number | null; volume_7d: number | null; volume_30d: number | null; sales_24h: number | null; sales_7d: number | null; sales_30d: number | null; top_bid_24h: number | null; top_bid_7d: number | null; top_bid_30d: number | null; owner_count: number | null; listed_count: number | null; market_cap: number | null; updated_at: string; created_at: string; [extra: string]: unknown; } ``` ### `MarketItem` Market item (NFT) shape. Reference items by `token_address` (Solana mint) or `${contract_address}:${token_id}` (EVM). ```ts export interface MarketItem { token_address: string; token_id: string | null; chain: Chain; name: string | null; image_url: string | null; animation_url: string | null; media_type: MediaType | string | null; thumbnail_url: string | null; /** * BENEFICIAL owner. **Nullable**: null means no beneficial owner is * known — typically the asset is held by a marketplace escrow, a stake pool or * a draw escrow. The raw on-chain holder is exposed separately as `raw_owner`. */ owner: string | null; /** The raw on-chain holder — an escrow, pool or draw account when `owner` is null. */ raw_owner?: string | null; listing_price: number | null; listing_currency: string | null; listing_marketplace: string | null; rarity_rank: number | null; rarity_score: number | null; rarity_tier: string | null; is_burned: boolean; /** Extra row fields (attributes, last_sale_price, etc.) may be present. */ [extra: string]: unknown; } ``` ### `MarketOrder` ```ts export interface MarketOrder { chain: Chain; order_type: OrderType; status: OrderStatus; maker: string; taker: string | null; token_address: string; token_id: string | null; price: number; currency: string; currency_contract: string | null; price_usd: number | null; expiration_at: string | null; protocol: string | null; source_marketplace: string; created_at: string; updated_at: string; } ``` ### `MediaType` ```ts export type MediaType = 'image' | 'gif' | 'video' | 'audio' | '3d_model' | 'html'; ``` ### `OffersSummaryResponse` ```ts export interface OffersSummaryResponse { floor_price: number | null; top_offer: CollectionOfferRow | null; collection_offers: CollectionOfferRow[]; trait_offers: TraitOfferRow[]; } ``` ### `OffsetPaginatedResponse` Offset-paginated envelope used by follows, comments, bundles. ```ts export interface OffsetPaginatedResponse { data: T[]; total?: number; limit: number; offset: number; } ``` ### `OffsetPaginationQuery` ```ts export interface OffsetPaginationQuery { limit?: number; offset?: number; } ``` ### `OnchainListingStatus` ```ts export interface OnchainListingStatus { status: string; statusCode: number; seller: string; price: number; priceLamports: string; currency: string; collectionKey: string; assetStandard: string; platformFeeBps: number; royaltyBps: number; createdAt: number; expiresAt: number; filledAt: number; } ``` ### `OnchainStatusResponse` ```ts export interface OnchainStatusResponse { assetId: string; onchain: boolean; listing: OnchainListingStatus | null; } ``` ### `OrderStatus` ```ts export type OrderStatus = 'active' | 'filled' | 'cancelled' | 'expired'; ``` ### `OrderType` ```ts export type OrderType = 'listing' | 'offer' | 'counter_offer' | 'collection_offer' | 'trait_offer' | 'item_offer'; ``` ### `PaginatedResponse` ```ts export type PaginatedResponse = CursorPaginatedResponse; ``` ### `PaginationQuery` ```ts export type PaginationQuery = CursorPaginationQuery; ``` ### `PlatformAnalyticsOverview` ```ts export interface PlatformAnalyticsOverview { total_collections: number; total_items: number; active_orders: number; total_users: number; period_volume: number; period_sales: number; } ``` ### `PlatformAnalyticsResponse` ```ts export interface PlatformAnalyticsResponse { overview: PlatformAnalyticsOverview; top_collections: TopCollectionRow[]; daily: PlatformDailyRow[]; period: string; } ``` ### `PlatformDailyRow` ```ts export interface PlatformDailyRow { date: string; volume: number; sales: number; listings: number; active_wallets: number; } ``` ### `PlatformStatusResponse` ```ts export interface PlatformStatusResponse { maintenance: boolean; maintenanceMessage: string; allowReads: boolean; minAppVersionIos: string | null; minAppVersionAndroid: string | null; updateMessage: string | null; } ``` ### `PriceHistoryPoint` ```ts export interface PriceHistoryPoint { t: string; price: number; currency: string; price_usd: number | null; } ``` ### `PriceHistoryResponse` ```ts export interface PriceHistoryResponse { data: PriceHistoryPoint[]; } ``` ### `PriceQuery` ```ts export interface PriceQuery { symbol: string; currency?: string; } ``` ### `PricesResponse` GET /platform/prices returns `{ prices: { [coingeckoId]: priceUsd|null } }`. ```ts export interface PricesResponse { prices: Record; } ``` ### `RequestInit` ```ts export interface RequestInit { query?: object; signal?: AbortSignal; } ``` ### `SearchCollectionHit` ```ts export interface SearchCollectionHit { name: string; slug: string; image_url: string | null; custom_image_url: string | null; chain: Chain; is_verified: boolean; contract_address: string; description: string | null; } ``` ### `SearchItemHit` ```ts export interface SearchItemHit { name: string | null; image_url: string | null; animation_url: string | null; media_type: string | null; thumbnail_url: string | null; chain: Chain; token_address: string; listing_price: number | null; listing_currency: string | null; rarity_rank: number | null; royalty_bps?: number; } ``` ### `SearchQuery` ```ts export interface SearchQuery { q: string; limit?: number; chain?: Chain; type?: 'collection' | 'item' | 'trait'; } ``` ### `SearchResponse` SDK-facing search response. Profiles and wallet-arrays are removed from the public API surface — only collection/item/trait results are returned. ```ts export interface SearchResponse { collections: SearchCollectionHit[]; items: SearchItemHit[]; traits: SearchTraitHit[]; } ``` ### `SearchTraitHit` ```ts export interface SearchTraitHit { chain: Chain; trait_type: string; trait_value: string; } ``` ### `SimilarItem` ```ts export interface SimilarItem { name: string; image_url: string | null; animation_url: string | null; media_type: string | null; thumbnail_url: string | null; token_address: string; token_id: string; listing_price: number | null; listing_currency: string | null; last_sale_price: number | null; last_sale_currency: string | null; rarity_rank: number | null; rarity_tier: string | null; chain: Chain; /** * The BENEFICIAL owner (, the #532 rule `MarketItem.owner` follows): `null` * when no beneficial owner is known — the item is held by a marketplace escrow, a stake pool * or a draw escrow. The raw on-chain holder is exposed separately as `raw_owner`. */ owner: string | null; /** The raw on-chain holder — may be an escrow or stake-pool account. */ raw_owner?: string | null; is_burned: boolean; market_collections: { name: string; image_url: string | null; chain: Chain; royalty_bps: number; }; } ``` ### `SimilarItemsQuery` ```ts export interface SimilarItemsQuery { /** How many similar items to return: default 12, clamped to 1..24 by the server. */ limit?: number; /** Which chain the item identifier refers to — see `ItemChainHint`. */ chain?: string; } ``` ### `SimilarItemsResponse` ```ts export type SimilarItemsResponse = SimilarItem[]; ``` ### `SmartMoneyFeedQuery` ```ts export interface SmartMoneyFeedQuery { chain?: Chain; cursor?: string; limit?: number; } ``` ### `SmartMoneyFeedResponse` ```ts export interface SmartMoneyFeedResponse { feed: SmartMoneyFeedRow[]; next_cursor: string | null; } ``` ### `SmartMoneyFeedRow` ```ts export interface SmartMoneyFeedRow { id: string; chain: Chain; collection_id: string | null; item_id: string | null; from_address: string; to_address: string; price: number | null; price_usd: number | null; currency: string | null; marketplace: string | null; event_time: string; tx_hash: string | null; side: 'buy' | 'sell'; collection: { id: string; name: string; slug: string; image_url: string | null; } | null; item: { id: string; name: string; image_url: string | null; } | null; } ``` ### `SmartMoneyInflowRow` ```ts export interface SmartMoneyInflowRow { collection_id: string; inflow_usd: number; buys: number; sells: number; last_event_at: string; collection: { id: string; name: string; slug: string; image_url: string | null; chain: Chain; } | null; } ``` ### `SmartMoneyInflowsQuery` ```ts export interface SmartMoneyInflowsQuery { period?: '24h' | '7d'; chain?: Chain | '__global__'; limit?: number; } ``` ### `SmartMoneyInflowsResponse` ```ts export interface SmartMoneyInflowsResponse { inflows: SmartMoneyInflowRow[]; period: string; chain: string; } ``` ### `SmartMoneyLeaderboardQuery` ```ts export interface SmartMoneyLeaderboardQuery { metric?: SmartMoneyMetric; period?: '24h' | '7d' | '30d'; chain?: Chain; limit?: number; } ``` ### `SmartMoneyLeaderboardResponse` ```ts export interface SmartMoneyLeaderboardResponse { rankings: SmartMoneyLeaderboardRow[]; metric: SmartMoneyMetric; period: string; } ``` ### `SmartMoneyLeaderboardRow` ```ts export interface SmartMoneyLeaderboardRow { address: string; chain: Chain; volume_usd?: number; realized_pnl_usd?: number; trades?: number; } ``` ### `SmartMoneyMetric` ```ts export type SmartMoneyMetric = 'volume' | 'realized_pnl' | 'trades'; ``` ### `SparklinesQuery` ```ts export interface SparklinesQuery { ids?: string[]; contract_addresses?: string[]; slugs?: string[]; hours?: number; days?: number; } ``` ### `TokenPricesQuery` ```ts export interface TokenPricesQuery { symbols?: string[]; } ``` ### `TokenPricesResponse` GET /platform/token-prices returns `{ [symbol]: priceUsd }` (e.g. `{ SOL: 84.52 }`). ```ts export type TokenPricesResponse = Record; ``` ### `TopCollectionRow` ```ts export interface TopCollectionRow { floor_price: number | null; floor_currency: string | null; volume_24h: number | null; volume_7d: number | null; volume_30d: number | null; sales_24h: number | null; sales_7d: number | null; sales_30d: number | null; owner_count: number | null; listed_count: number | null; name: string; slug: string; image_url: string | null; chain: Chain; is_verified: boolean; contract_address: string | null; } ``` ### `TraitOfferRow` ```ts export interface TraitOfferRow { trait_type: string; trait_value: string; price: number; currency: string; fillable_offers: number; total_size: number; bidders: number; marketplace: string; } ``` ### `TraitPricingRow` ```ts export interface TraitPricingRow { trait_type: string; trait_value: string; floor_price: number | null; currency: string | null; top_trait_offer: number | null; offer_currency: string | null; listed_count: number; sales_7d: number; } ``` ### `TraitSearchGroup` ```ts export interface TraitSearchGroup { chain: Chain; items: SearchTraitHit[]; count: number; collection_name?: string; collection_image?: string; is_verified?: boolean; } ``` ### `TraitSearchQuery` ```ts export interface TraitSearchQuery { q: string; limit?: number; chain?: Chain; } ``` ### `TraitSearchResponse` ```ts export interface TraitSearchResponse { results: TraitSearchGroup[]; total_matches: number; } ``` ### `TrustCollectionRow` ```ts export interface TrustCollectionRow { id: string; name: string; slug: string; chain: Chain; image_url: string | null; is_verified: boolean; contract_address: string | null; wash_pct: number; wash_volume_usd: number; total_volume_usd: number; clean_volume_usd: number; flagged_trade_count: number; } ``` ### `TrustCollectionsQuery` ```ts export interface TrustCollectionsQuery { sort?: TrustSort; period?: '24h' | '7d' | '30d'; chain?: Chain; limit?: number; } ``` ### `TrustCollectionsResponse` ```ts export interface TrustCollectionsResponse { collections: TrustCollectionRow[]; period: string; sort: TrustSort; } ``` ### `TrustSort` ```ts export type TrustSort = 'cleanest' | 'most_suspicious'; ``` ### `TrustWalletResponse` ```ts export interface TrustWalletResponse { address: string; wash_pct_7d: number | null; wash_pct_30d: number | null; wash_pct_all: number | null; total_trades_30d: number; total_trades_all: number; in_cluster: boolean; /** Verified-treasury memberships — present when the wallet is on the * admin-approved allowlist. UI uses this to render the green badge and to * link to the scoped collection page when length === 1. */ verified_treasury: TrustWalletTreasuryEntry[]; /** Auto-classified distributor flag — true when this wallet's recent * activity is dominated by buys + outgoing transfers (treasury-like * pattern) but no admin approval exists. UI renders a quieter cyan chip. */ is_distributor: boolean; } ``` ### `TrustWalletTreasuryEntry` Verified-treasury entry surfaced on a wallet. Empty array when the wallet is not on the allowlist. `collection_id` null = global treasury (every collection); non-null = scoped to that specific collection. ```ts export interface TrustWalletTreasuryEntry { label: string | null; collection_id: string | null; collection_name: string | null; collection_slug: string | null; } ``` ### `WalletLotRow` ```ts export interface WalletLotRow { id: string; item_id: string; collection_id: string | null; chain: Chain; acquired_at: string; cost_basis_usd: number | null; disposed_at: string | null; disposed_price_usd: number | null; realized_pnl_usd: number | null; holding_period_seconds: number | null; } ``` ### `WalletLotSummary` ```ts export interface WalletLotSummary { item_id: string; collection_id: string | null; realized_pnl_usd: number | null; disposed_at: string; holding_period_seconds: number | null; } ``` ### `WalletPnLDailyRow` ```ts export interface WalletPnLDailyRow { date: string; realized_pnl_usd: number; volume_usd: number; realized_trade_count: number; } ``` ### `WalletPnLLotsQuery` ```ts export interface WalletPnLLotsQuery { closed_only?: boolean; cursor?: string; limit?: number; } ``` ### `WalletPnLLotsResponse` ```ts export interface WalletPnLLotsResponse { lots: WalletLotRow[]; next_cursor: string | null; } ``` ### `WalletPnLQuery` ```ts export interface WalletPnLQuery { period?: '7d' | '30d' | '90d' | 'ytd' | 'all'; } ``` ### `WalletPnLResponse` ```ts export interface WalletPnLResponse { address: string; period: string; realized_pnl_usd: number; unrealized_pnl_usd: number; volume_usd: number; total_trades: number; win_rate_pct: number; open_lots_count: number; open_lots_value_usd: number; daily: WalletPnLDailyRow[]; best_trades: WalletLotSummary[]; worst_trades: WalletLotSummary[]; } ``` --- Source: https://docs.deads.io/sdk/gravemarket-reference Markdown: https://docs.deads.io/sdk/gravemarket-reference.md --- DOCUMENT: https://docs.deads.io/sdk/gravemarket.md # GraveMarket SDK **`@solanadeads/gravemarket`** · version **0.3.1** · install with `npm i @solanadeads/gravemarket` Guide adapted from the published README for clarity. Code examples are preserved. Every method, with its signature and types: [method reference](https://docs.deads.io/sdk/gravemarket-reference.md). Read-only TypeScript SDK for the [GraveMarket](https://gravemarket.io) marketplace public API. Wraps public `GET` endpoints and returns fully typed responses. - **Zero runtime dependencies.** Native `fetch`. Works in Node 18+, browsers, Cloudflare Workers, Deno, and Bun. - **Typed.** Full `.d.ts` for every method and response. - **Cursor pagination.** Async iterator helpers for every paged endpoint. ## Install ```bash npm install @solanadeads/gravemarket # or pnpm add @solanadeads/gravemarket ``` Requires **Node 18+** (uses native `fetch` and `AbortController`). ## Quick start ```ts import { GravemarketClient } from '@solanadeads/gravemarket'; const client = new GravemarketClient(); // Browse collections — reference by slug or contract address const page = await client.collections.list({ limit: 25, chain: 'solana-mainnet' }); const first = page.data[0]; console.log(first.name, first.slug, first.contract_address, first.floor_price); // Fetch an NFT — by mint address (Solana) or `contract:tokenId` (EVM) const item = await client.items.get(''); // Just the rarity rank/score/tier const rarity = await client.items.rarity(''); // { rank: 12, score: 184.2, tier: 'legendary', total_supply: 5000 } ``` > Legacy `Graveyard*`-prefixed exports (e.g. `GraveyardClient`, `GraveyardApiError`) remain available as aliases for backward compatibility with existing integrations. ## Options ```ts new GravemarketClient({ apiKey: 'gm_...', // recommended — get one at https://gravemarket.io/developers baseUrl: 'https://api.deads.io', // default (api.solanadeads.com serves the same API) timeoutMs: 30_000, // default 30s fetch: customFetch, // optional — defaults to globalThis.fetch headers: { 'X-My-Header': '1' }, // optional default headers }); ``` ## API keys The SDK works anonymously by default, but **applying for an API key is recommended — anonymous access will be disabled in the future.** Generate one at [gravemarket.io/developers](https://gravemarket.io/developers) and pass it via the `apiKey` option. The SDK sends it as the `X-API-Key` header on every request. When the API responds with HTTP 429, the SDK throws a `GravemarketRateLimitError` exposing `retryAfterMs`, `limit`, and `remaining`: ```ts import { GravemarketRateLimitError } from '@solanadeads/gravemarket'; try { await client.collections.list(); } catch (err) { if (err instanceof GravemarketRateLimitError) { await new Promise((r) => setTimeout(r, err.retryAfterMs ?? 1000)); // retry } } ``` ## Namespaces | Namespace | Methods | |---|---| | `client.collections` | `meta`, `launches`, `sparklines`, `list`, `get`, `items`, `activity`, `stats`, `traits`, `holders`, `offers`, `traitFloors`, `traitPricingSummary`, `crossChain`, `analytics`, `floor`, `listAll`, `activityAll` | | `client.items` | `get`, `activity`, `offers`, `listings`, `traits`, `neighbors`, `priceHistory`, `rarity` | | `client.orders` | `aggregatedListings`, `bestPrice`, `aggregatedFloor`, `offersSummary`, `onchainStatus` | | `client.activity` | `list`, `listAll` | | `client.search` | `query`, `traits` | | `client.analytics` | `platform`, `collection`, `compare`, `heatmap`, `flow` | | `client.smartMoney` | `feed`, `leaderboard`, `inflows` | | `client.walletAnalytics` | `pnl`, `pnlLots` | | `client.trust` | `collections`, `wallet` | | `client.recommendations` | `similar` | | `client.platform` | `tokenPrices`, `price`, `tokenPrice`, `fees`, `fee`, `chains`, `branding`, `analyticsConfig`, `status` | | `client.affiliates` | `config`, `leaderboard` | | `client.config` | `client` | ### Smart Money Track what the top wallets are doing right now. ```ts // Live whale activity feed (cursor-paginated) const { feed, next_cursor } = await client.smartMoney.feed({ chain: 'solana-mainnet', limit: 25 }); for (const row of feed) { console.log(row.side, row.collection?.name, row.price_usd, row.from_address); } // Top whales by metric over period const { rankings } = await client.smartMoney.leaderboard({ metric: 'realized_pnl', period: '30d' }); // Collections with the most net whale inflow const { inflows } = await client.smartMoney.inflows({ period: '7d' }); ``` ### Wallet PnL Per-wallet profit/loss with FIFO lot tracking. ```ts const summary = await client.walletAnalytics.pnl(wallet, { period: '30d' }); console.log(summary.realized_pnl_usd, summary.unrealized_pnl_usd, summary.win_rate_pct); // Paginate the underlying lots (open + closed) const lots = await client.walletAnalytics.pnlLots(wallet, { closed_only: true, limit: 100 }); ``` ### Trust Wash-trade fingerprints for collections and wallets. ```ts // Top trustworthy / suspicious collections const cleanest = await client.trust.collections({ sort: 'cleanest', period: '7d', limit: 20 }); const sus = await client.trust.collections({ sort: 'most_suspicious', period: '30d' }); // Wallet wash-rate (returns null pct for wallets under 10 trades) const t = await client.trust.wallet(wallet); console.log(t.wash_pct_30d, t.in_cluster); ``` ### Adjusted volume toggle Platform / collection analytics accept `volume: 'reported' | 'adjusted'` (default `adjusted`). Adjusted volume excludes sales flagged by the wash-trade detector. ```ts const reported = await client.analytics.platform({ period: '7d', volume: 'reported' }); const adjusted = await client.analytics.platform({ period: '7d' }); // adjusted by default ``` ### Collection-level analytics ```ts // Daily series + stats snapshot + holder history for a collection const a = await client.analytics.collection('apes-collection', { period: '30d' }); // Side-by-side comparison of up to 4 collections const cmp = await client.analytics.compare({ collection_ids: 'a,b,c', period: '7d' }); // 7×24 liquidity heatmap (UTC) per chain × window const h = await client.analytics.heatmap({ chain: 'solana-mainnet', metric: 'volume', days: 30 }); // Net flow / buyer-seller mix / hold-time histogram for a collection const f = await client.analytics.flow('apes-collection-uuid', { period: '7d' }); ``` ## Identifying entities Reference entities by their public identifiers: | Entity | Identifier | |---|---| | Collection | `slug` or `contract_address` | | Item (NFT) | `token_address` (Solana mint), or `contract_address:token_id` (EVM) | | Wallet | `wallet_address` | | Affiliate | `affiliate_code` | | Trait | `trait_type` + `trait_value` | ## Pagination Cursor-paginated endpoints return `{ data, cursor, hasMore }`. Several namespaces expose `*All` methods returning async iterators: ```ts for await (const batch of client.collections.listAll({ chain: 'solana-mainnet' })) { for (const c of batch) console.log(c.slug, c.floor_price); } ``` Or use `paginate()` directly: ```ts import { paginate } from '@solanadeads/gravemarket'; for await (const batch of paginate((cursor) => client.activity.list({ cursor, type: 'sale', limit: 100 }), )) { // ... } ``` ## Error handling ```ts import { GravemarketApiError, GravemarketTimeoutError } from '@solanadeads/gravemarket'; try { await client.items.get('does-not-exist'); } catch (err) { if (err instanceof GravemarketTimeoutError) { // request timed out } else if (err instanceof GravemarketApiError) { console.error(err.code, err.status, err.message); } else { throw err; } } ``` The SDK auto-unwraps the `{ success, data }` envelope. If the server returns `success: false`, a `GravemarketApiError` is thrown with the server's `code`, `message` and (when it sends one) `details`. ### One contract, several chains A CREATE2 contract can live at the same address on more than one chain, so an EVM `contract:token_id` can name more than one NFT. The API never guesses: it answers `409` `AMBIGUOUS_ITEM`, with the chains in `details.candidates`. Pass the one you mean: ```ts const WANTED_CHAIN = 'robinhood-mainnet'; // the chain YOUR app means — never "the first one" try { await client.items.get('0xabc…:7'); } catch (err) { if (err instanceof GravemarketApiError && err.code === 'AMBIGUOUS_ITEM') { const { candidates } = err.details as { candidates: { chain: string }[] }; if (candidates.some((c) => c.chain === WANTED_CHAIN)) { await client.items.get('0xabc…:7', { chain: WANTED_CHAIN }); } } } ``` If you already know the chain, pass `{ chain }` up front and skip the round trip. Every `client.items` method and `client.recommendations.similar` accept `chain`. ## License MIT --- Source: https://docs.deads.io/sdk/gravemarket Markdown: https://docs.deads.io/sdk/gravemarket.md --- DOCUMENT: https://docs.deads.io/sdk/gravemint-reference.md # 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](https://docs.deads.io/sdk/gravemint.md). ```ts import { GraveMintClient } from "@solanadeads/gravemint"; const client = new GraveMintClient({ apiKey: "gm_pub_..." }); ``` ## Contents - [`client.platform`](#client-platform) — 2 methods - [`client.discovery`](#client-discovery) — 7 methods - [`client.collections`](#client-collections) — 3 methods - [`client.nfts`](#client-nfts) — 1 method - [`client.bounty`](#client-bounty) — 2 methods - [`client.codes`](#client-codes) — 1 method - [`client.v1`](#client-v1) — 19 methods - [`client.mint`](#client-mint) — 4 methods - [Functions](#functions) — 11 - [Classes](#classes) — 12 - [Types](#types) — 68 ## `client.platform` > First-party only — not usable with a partner key. See [Legacy read API](https://docs.deads.io/sdk/gravemint.md#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; ``` ### `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; ``` ## `client.discovery` > First-party only — not usable with a partner key. See [Legacy read API](https://docs.deads.io/sdk/gravemint.md#legacy-read-api). ### `discovery.getFeatured()` GET /featured — `{success, collections: [...]}` ```ts getFeatured(filters?: DiscoveryFilters, options?: RequestOptions): Promise; ``` ### `discovery.getLiveMints()` GET /live-mints — `{success, mints: [...]}` ```ts getLiveMints(filters?: DiscoveryFilters, options?: RequestOptions): Promise; ``` ### `discovery.getTrending()` GET /trending — `{success, type, collections: [...]}` ```ts getTrending(filters?: DiscoveryFilters, options?: RequestOptions): Promise; ``` ### `discovery.getUpcoming()` GET /upcoming-mints — `{success, mints: [...]}` ```ts getUpcoming(filters?: DiscoveryFilters, options?: RequestOptions): Promise; ``` ### `discovery.getRecentSoldouts()` GET /recent-soldouts — `{collections: [...]}` (no success wrapper) ```ts getRecentSoldouts(filters?: DiscoveryFilters, options?: RequestOptions): Promise; ``` ### `discovery.listCollections()` GET /collections — `{success, collections: [...]}` ```ts listCollections(params?: DiscoveryFilters & { offset?: number; status?: "active" | "ended" | "upcoming"; }, options?: RequestOptions): Promise; ``` ### `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; ``` ## `client.collections` > First-party only — not usable with a partner key. See [Legacy read API](https://docs.deads.io/sdk/gravemint.md#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; ``` ### `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; ``` ### `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; ``` ## `client.nfts` > First-party only — not usable with a partner key. See [Legacy read API](https://docs.deads.io/sdk/gravemint.md#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; ``` ## `client.bounty` > First-party only — not usable with a partner key. See [Legacy read API](https://docs.deads.io/sdk/gravemint.md#legacy-read-api). ### `bounty.getSummary()` ```ts getSummary(identifier: CollectionIdentifier, options?: RequestOptions): Promise; ``` ### `bounty.getPrizes()` ```ts getPrizes(identifier: CollectionIdentifier, options?: RequestOptions): Promise; ``` ## `client.codes` > First-party only — not usable with a partner key. See [Legacy read API](https://docs.deads.io/sdk/gravemint.md#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; ``` ## `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; ``` ### `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; ``` ### `v1.gallery()` 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; ``` ### `v1.recentlyMinted()` The live "just minted" feed for the drop — the same one gravemint.io renders. ```ts recentlyMinted(identifier: string, params?: { limit?: number; }): Promise; ``` ### `v1.bounty()` Bounty summary for the drop, when it has one. ```ts bounty(identifier: string): Promise; ``` ### `v1.bountyPrizes()` The bounty's public prize table. ```ts bountyPrizes(identifier: string): Promise; ``` ### `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; ``` ### `v1.claimCodes()` Does this drop use claim codes, and of what kind? ```ts claimCodes(collectionId: string): Promise; ``` ### `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; ``` ### `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; ``` ### `v1.tokenPrices()` Platform token prices — what an SPL-priced drop is worth in USD. Global, no drop. ```ts tokenPrices(): Promise; ``` ### `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; ``` ### `v1.dutchPrice()` The live price of a dynamic Dutch phase, which moves with time. ```ts dutchPrice(phaseId: string): Promise; ``` ### `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; ``` ### `v1.traits()` Trait names and values, for filtering a gallery. ```ts traits(identifier: string): Promise; ``` ### `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; ``` ### `v1.topHolders()` Largest holders — social proof for a mint page. ```ts topHolders(identifier: string): Promise; ``` ### `v1.topMinters()` Who minted the most. ```ts topMinters(identifier: string): Promise; ``` ### `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; ``` ### `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; ``` ### `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; ``` ### `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; ``` ## 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(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(path: string, options?: RequestOptions): Promise; post(path: string, body: unknown, options?: RequestOptions): Promise; 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; /** 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; raffles?: { twitterRequirements: boolean; maxActiveRaffles: number; }; enabledChains?: Record; [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; bogo?: Record | 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; /** 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; /** Per-symbol source (e.g. "coingecko", "jupiter", "unavailable"). */ sources: Record; /** 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; /** * 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; } ``` ### `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; /** 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` 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>; } | 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`; 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; 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; sources: Record; /** 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; /** 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; 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; 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; } ``` --- Source: https://docs.deads.io/sdk/gravemint-reference Markdown: https://docs.deads.io/sdk/gravemint-reference.md --- DOCUMENT: https://docs.deads.io/sdk/gravemint.md # GraveMint SDK **`@solanadeads/gravemint`** · version **1.3.0** · install with `npm i @solanadeads/gravemint` Guide adapted from the published README for clarity. Code examples are preserved. Every method, with its signature and types: [method reference](https://docs.deads.io/sdk/gravemint-reference.md). The GraveMint SDK provides collection reads and wallet-signed mint flows for your application. It uses native `fetch`, includes TypeScript declarations and supports ESM and CommonJS. Bring your existing wallet library; no chain SDK is bundled. The versioned API is additive-only. See [Versioning](#versioning). ```bash npm install @solanadeads/gravemint ``` Partner keys are invite-only. Request access for your collections and origins; see [Getting a key](#getting-a-key). --- ## Transaction submission {#the-one-rule-worth-reading-first} `prepare()` builds the transaction. Your wallet signs it, and `execute()` returns the signed bytes to GraveMint for validation and submission. GraveMint checks the prepared instructions, accounts and amounts before broadcasting. Use the SDK's execute methods rather than broadcasting through your wallet library. --- ## Contents - [Quick start](#quick-start) · [Getting a key](#getting-a-key) · [Configuration](#configuration) - [Building a mint page](#building-a-mint-page) — [phases](#phases-and-countdowns) · [price](#price-is-a-shape-not-a-number) · [eligibility](#eligibility) · [capabilities](#capabilities--when-you-cannot-render-a-drop) - [Minting](#minting) — [the signer](#the-signer) · [wallet-adapter example](#a-real-signer-solanawallet-adapter) - [Errors and retries](#errors-and-retries) · [Rate limits](#rate-limits) - [Versioning](#versioning) · [Legacy read API](#legacy-read-api) · [Security](#security-notes-for-partners) --- ## Quick start ```ts import { GraveMintClient } from "@solanadeads/gravemint"; const gm = new GraveMintClient({ apiKey: "gm_pub_..." }); // 1. Everything needed to render the drop, in one call. const drop = await gm.v1.collection("deads"); console.log(drop.collection.name); console.log(drop.stats.mintedCount, "/", drop.stats.totalSupply); // 2. Which phase is live right now (server-decided, not client-computed). const phase = drop.phases.active[0]; // 3. Can this wallet mint it? const verdict = await gm.v1.eligibility(drop.collection.id, phase.id, wallet); // 4. Mint. You supply the signer; GraveMint broadcasts. const result = await gm.mint.mint({ collectionId: drop.collection.id, phaseId: phase.id, quantity: 1, walletAddress: wallet, signer, }); ``` --- ## Getting a key Request a partner key for the collections and origins your integration will use. Choose from three key classes: | Class | Prefix | Where it belongs | What bounds it | |---|---|---|---| | Publishable | `gm_pub_` | your web page — it is *meant* to be visible | the origin allow-list on the key | | Secret | `gm_live_` | your server only | secrecy — it has no origin binding | | Sandbox | `gm_test_` | development against test-network drops only | refused on mainnet (`SANDBOX_KEY_ON_MAINNET`); production keys are refused on test networks (`LIVE_KEY_ON_TESTNET`) | Publishable keys are intended for browser bundles. Register every requesting origin, including staging and previews, and keep the collection scope limited to your integration. Keep `gm_live_` keys on your server. They do not use browser-origin binding. Use `gm_live_` for server calls. Browser keys require a matching `Origin` header; a request without one returns `ORIGIN_NOT_ALLOWED`. --- ## Configuration ```ts new GraveMintClient({ apiKey: "gm_pub_...", // required timeoutMs: 30_000, maxRetries: 3, // 429 / 5xx / network rateLimit: { requestsPerSecond: 5, burst: 10 }, // null to disable fetch: globalThis.fetch, }); ``` ### Base URLs {#base-urls-—-there-are-two-on-purpose} The versioned contract is a separate mount from the legacy public read API, with its own auth and its own middleware. The client keeps two base URLs: | | default | used by | |---|---|---| | `baseUrl` | `https://api.deads.io/gravemint/public` (`api.solanadeads.com` serves the same origin and keeps working) | the [legacy read API](#legacy-read-api) | | `v1BaseUrl` | `https://api.solanadeads.com/gravemint/v1` | `gm.v1`, `gm.mint` | You normally set neither. If you set `baseUrl` to something ending in `/public`, `v1BaseUrl` is derived for you. If it ends in anything else — because you are routing through your own proxy — the constructor throws and asks you to pass `v1BaseUrl` explicitly. Explicit proxy configuration lets the client validate its API routes at startup. ```ts // Proxying through your own backend: new GraveMintClient({ apiKey, v1BaseUrl: "https://yoursite.com/api/gravemint" }); ``` Node `<18` needs a `fetch` polyfill: `new GraveMintClient({ apiKey, fetch })`. --- ## Building a mint page Read collection data, phases, eligibility and supported features before enabling minting. `gm.v1.collection(identifier)` returns all of it in one call. `identifier` is a `shortId`, a collection UUID, or an on-chain address. Which one to use: - Anything a person sees (URLs, share links): the `shortId` or the on-chain address. - API calls: `drop.collection.id`, the UUID. Every v1 route accepts it, so passing it everywhere is always safe. Until gravemint#4493, some read routes answered a UUID with 404; they now accept it. - `gm.mint.prepare()` (and `gm.mint.mint()`) also take the `shortId` or on-chain address as `collectionId` (gravemint#4525). It is resolved within your key's own drops only; anything else is refused as not in scope. Pass an address exactly as v1 returned it: it is matched as-is, not case-folded. If two of your drops share an on-chain address, that address resolves to neither, so use the `shortId` or UUID. ```ts const drop = await gm.v1.collection("deads"); drop.collection // name, symbol, image, chain, socials, isVerified drop.stats // totalSupply, mintedCount, availableCount, percentMinted drop.phases // { all, active, upcoming } drop.capabilities // can you render this drop faithfully? drop.serverTime // our clock — see below ``` ### Server-resolved values {#the-rule-that-keeps-you-correct} Use the returned pricing, eligibility and phase status values. GraveMint evaluates the collection and wallet settings on the server; refresh these reads when the wallet, selected phase or mint state changes. ### Phases and countdowns ```ts for (const phase of drop.phases.all) { phase.status // "active" | "upcoming" | "ended" phase.name phase.startDate // ISO, or null phase.endDate phase.maxPerWallet // null = no cap phase.maxPerTransaction phase.phaseSupply phase.isGated // does it have eligibility requirements? phase.bogo // { buy, get } or null phase.msUntilStart // ← render countdowns from these phase.msUntilEnd } ``` `phases.all` includes ended phases, which you can display in the full schedule or filter from an active view. Use `msUntilStart` and `msUntilEnd` for countdowns. Anchor a ticking display to `drop.serverTime`, then refresh the collection response when a countdown expires. ### Price is a shape, not a number ```ts type PriceDisplay = | { kind: "amount"; amount: number; currency: string; isFree: boolean; approximate?: boolean } | { kind: "range"; min: number; max: number; currency: string } | { kind: "hidden" } | { kind: "unknown" }; ``` Render it by branching, and treat all four as normal: ```tsx switch (phase.priceDisplay.kind) { case "amount": return phase.priceDisplay.isFree ? <>Free : <>{phase.priceDisplay.approximate ? "~" : ""}{phase.priceDisplay.amount} {phase.priceDisplay.currency}; case "range": return <>{phase.priceDisplay.min}–{phase.priceDisplay.max} {phase.priceDisplay.currency}; case "hidden": return <>Price revealed when you qualify; case "unknown": return <>Price shown at checkout; } ``` `priceDisplay` represents the price available for display before a mint is prepared. It accounts for the phase's pricing settings. Hidden prices remain hidden until the wallet qualifies; unknown prices have no display amount for the request. Show “Free” only for an amount with `isFree: true`. Do not replace hidden or unknown values with zero. The final wallet-specific breakdown is returned by `prepare()` as `pricing`, including applicable benefits and fees. ### Eligibility ```ts const v = await gm.v1.eligibility(collectionId, phaseId, walletAddress); v.gated // does this phase gate at all? v.meetsRequirements // the verdict v.onAllowlist v.spotsAllocated v.spotsRemaining v.requirements // [{ type, met, description }] — render these v.phase // { id, name, hasStarted, hasEnded } v.message ``` This is server-evaluated because it has to be: the dominant gate is an NFT-holding check needing an on-chain lookup, and an allowlist must never be published to a client. `meetsRequirements` reports wallet requirements separately from the time window. Check `phase.hasStarted` and `phase.hasEnded` as well before enabling minting. For ungated phases, use `gated: false` as the requirements check. ```ts const canMintNow = v.meetsRequirements && v.phase.hasStarted && !v.phase.hasEnded; ``` ### The rest of the drop — art, feed, bounty, per-wallet counts These are all scoped to your key the same way `collection()` is: a key issued for another drop gets `403 COLLECTION_NOT_IN_SCOPE`, never an empty list. ```ts await gm.v1.gallery("deads", { limit: 24 }); // NFTs still available to mint await gm.v1.recentlyMinted("deads"); // the live "just minted" feed await gm.v1.bounty("deads"); // bounty summary, when there is one await gm.v1.bountyPrizes("deads"); // its public prize table await gm.v1.walletMints("deads", wallet); // "you have minted 2 of 3", per phase ``` `walletMints` includes bonus mints such as BOGO and bounty mints. Eligibility uses separate allocation rules. Use the wallet-mint response when displaying minted or remaining counts. ### Claim codes Claim-code endpoints describe the collection’s code configuration and the benefits a wallet has already redeemed. ```ts await gm.v1.claimCodes(collectionId); // does this drop use codes? await gm.v1.claimCodeBenefits(collectionId, wallet); // what has this wallet earned? await gm.v1.validateClaimCode(collectionId, 'CODE-123'); // { valid, grantType, ... } ``` `validateClaimCode` checks a code without redeeming it or consuming a use. A code outside the collection returns the same response as an unknown code. `prepare` applies benefits already redeemed by the minting wallet for this collection. It does not accept a claim code. The collector redeems codes on the hosted GraveMint page, where the required checks and signature are handled. To display benefits before minting, call `claimCodeBenefits(collectionId, wallet, { phaseId })`. Include the selected phase and, for a pack drop, `packTierId` to restrict the calculation appropriately. The `prepare` response's `pricing` remains the final amount. ### Pricing helpers ```ts await gm.v1.tokenPrices(); // platform token prices, for an SPL-priced drop await gm.v1.peggedPrice(phaseId); // resolved amount for a USD/native-pegged phase await gm.v1.dutchPrice(phaseId); // live price of a dynamic Dutch phase ``` Render `phase.priceDisplay` — it already resolves Dutch and pegged prices (a pegged figure comes back `approximate`). v1 phases carry no raw `price` field; these two helpers are for refreshing a live number without re-reading the whole drop. Both phase routes are scoped through the phase's own collection: a phase id is not a way around collection scope. ### Supply, traits and social proof ```ts await gm.v1.availability(collectionId); // approximate supply counts — a progress bar, never a sold-out decision await gm.v1.traits(identifier); // trait names and values, for gallery filters await gm.v1.allMinted(identifier, { limit: 100, offset: 0 }); // ONE page (default 20, max 100) — page with offset await gm.v1.topHolders(identifier); // largest holders await gm.v1.topMinters(identifier); // who minted the most ``` ### Capabilities — when you cannot render a drop ```ts drop.capabilities // { requiresFeatures: string[], supportedBySurface: boolean, mintUrl: string | null } ``` v1 covers core mint: supply, phases, fixed and pegged pricing (including BOGO and hidden prices), claim-code benefits already redeemed, eligibility, per-wallet limits, and minting on Solana. `requiresFeatures` names what a drop needs beyond that; any of these makes `supportedBySurface` false: | Feature | Why the drop is not core-renderable | |---|---| | `packs` | a phase sells pack tiers | | `gallery_mode` | the collector chooses which NFT to mint | | `nft_selection` | a phase restricts which NFTs can be minted (by rarity, trait, or a specific list) | | `generative` | a generative collection — traits are composed at mint and the price carries a surcharge | | `dutch_auction`, `dynamic_dutch` | the live price decays or surges, so `priceDisplay` is a range or `unknown`, not a price tag | | `cross_chain` | payment on another chain, which partner surfaces never support | | `non_solana_chain` | the drop is not on Solana — v1 mints Solana only | | `claim_codes` | the drop has active claim codes (a collection-level fact — codes are redeemed against the drop) | This list only grows. Branch on `supportedBySurface`, not on the names. ```tsx if (!drop.capabilities.supportedBySurface) { return Mint on GraveMint; } ``` Handle unsupported capabilities before enabling minting, so collectors can use the hosted page for features outside your integration. Collections with unrecognized requirements are marked unsupported. Use `mintUrl` when provided to open the hosted collection page. --- ## Minting ```ts const prepared = await gm.mint.prepare({ collectionId, phaseId, quantity: 1, walletAddress, affiliateCode, // optional; validated server-side, ignored if unused walletProvider: wallet.adapter.name, // optional; fills your wallet breakdown, else "Unknown" }); const signed = await signer.signTransaction(prepared.transactions[0].transaction); const result = await gm.mint.execute({ sessionId: prepared.sessions[0].sessionId, signedTransaction: signed, }); ``` `prepare` returns `transactions[]` and `sessions[]`, including for quantity one. Read `sessions[0].sessionId` for the first transaction. `mint()` handles the pairing for you. Or in one call: ```ts const result = await gm.mint.mint({ collectionId, phaseId, quantity: 1, walletAddress, signer, }); ``` `prepare()` does the real work — eligibility, limits, pricing, supply reservation, transaction build — and returns `pricing` (the authoritative breakdown), `sessions[]` (one per transaction), `expiresAt` / `expiresInMs`, and `quantityAdjusted` — true when fewer NFTs were prepared than requested (a wallet or phase limit, or supply). Tell the collector before they sign; `adjustmentReason` says why. `execute` waits up to 130 seconds by default — longer than the server's own budget, which can still be broadcasting after a shorter client gives up — unless you set a client-wide `timeoutMs`, which is respected. It is never retried automatically: re-sending signed transactions gets `ALREADY_PROCESSING` / `ALREADY_COMPLETED` back, which would hide whether the first attempt landed. If `gm.mint.mint()` loses the answer AFTER the signed transaction left your client — a timeout, a dropped connection, any 5xx (including an edge timeout such as Cloudflare's 524), the server's own `408`, or `ALREADY_PROCESSING` / `ALREADY_COMPLETED` / `RECONCILE_PENDING` / `MINT_PAYMENT_UNVERIFIED` — it throws `MintOutcomeUnknownError` (`code: "OUTCOME_UNKNOWN"`, `sessionIds`). The mint may still land. Do not retry: a retry prepares again and can mint twice. Show its message ("check your wallet before trying again"). A 5xx refused *before* broadcast looks the same from here, so it is reported as unknown too — the safe direction. `mint()` also *returns* (does not throw) when a batch confirmed only some, or none, of its transactions: `{ success: false, partial, message }`. Anything but `success: true` is not a success. Sessions expire. Do not hold a signature and submit it later; prepare again. A prepared session also reserves supply, so do not call `prepare()` just to read a price — that is what `priceDisplay` is for. ### The signer ```ts interface TransactionSigner { signTransaction(base64Transaction: string): Promise; } ``` The signer accepts a base64 transaction and returns the signed transaction in base64. You can also supply `signAllTransactions` to request batch approval. ### Wallet-adapter example {#a-real-signer-solana-wallet-adapter} ```ts import { VersionedTransaction } from "@solana/web3.js"; const signer = { async signTransaction(base64: string) { const tx = VersionedTransaction.deserialize(Buffer.from(base64, "base64")); const signed = await wallet.signTransaction(tx); return Buffer.from(signed.serialize()).toString("base64"); }, }; ``` For legacy transaction support and browser Buffer setup, see [the signer adapter](https://docs.deads.io/guides/signing.md#solana-wallet-adapter). ### Chain support The v1 mint flow supports Solana. Check `capabilities.supportedBySurface` before preparation and offer `mintUrl` for collections that require another flow. --- ## Errors and retries Every error carries a machine-readable `code`, a human `message` safe to show a collector, plus `.status` and `.requestId` for support. ```ts import { GraveMintError, RateLimitError } from "@solanadeads/gravemint"; try { await gm.mint.mint({ ... }); } catch (err) { if (err instanceof GraveMintError) { console.error(err.code, err.requestId); showToast(err.message); // safe: no internals, no schema details } } ``` ### Error handling {#🚨-read-this-before-branching-on-a-code} The API's stable v1 error catalog covers authentication, scope and collection lookups. Mint handlers and middleware can return additional codes, including `NO_NFTS_AVAILABLE`, `PHASE_ENDED`, `WALLET_LIMIT_EXCEEDED`, `SESSION_NOT_FOUND`, `INSUFFICIENT_FUNDS` and `NOT_WHITELISTED`. Handle known codes explicitly and keep a message fallback for additional or code-less errors. For batch outcomes, read each transaction's `errorCode` from the returned `results` array. A batch HTTP response can succeed even when individual transactions fail or remain pending. ```ts import { isBatchMintResult } from "@solanadeads/gravemint"; const res = await gm.mint.mint({ ... }); if (isBatchMintResult(res) && res.success === false) { // res.partial: did SOME land? res.minted of res.requested; res.message is safe to show. for (const r of res.results) { if (!r.success) console.warn(r.sessionId, r.errorCode); // e.g. TX_MODIFIED } } ``` Branch on documented stable codes where your application needs specific recovery behavior. Display the returned message for other responses. ```ts const code = err instanceof GraveMintError ? err.code : undefined; if (code === "TX_MODIFIED") { /* never retry — see below */ } else if (code === "ORIGIN_NOT_ALLOWED") { /* config: send us the origin */ } else { showToast(err.message); } // covers the unfrozen and code-less cases ``` ### Stable v1 codes {#frozen-—-v1-s-own-codes} | Code | Meaning | Retry? | |---|---|---| | `COLLECTION_NOT_FOUND` | no such drop, or not publicly servable | no | | `LOOKUP_FAILED` | the collection lookup could not complete | yes, backoff | | `COLLECTION_REQUIRED` | the request named no collection | no — fix the call | | `CODE_REQUIRED` | a claim-code check arrived with no code | no — fix the call | | `API_KEY_REQUIRED` / `API_KEY_INVALID` | missing or wrong key | no — fix config | | `API_KEY_INACTIVE` / `API_KEY_EXPIRED` | key revoked or lapsed | no — talk to us | | `ORIGIN_NOT_ALLOWED` | valid key, unregistered origin (or a `gm_pub_` key used server-side) | no | | `COLLECTION_NOT_IN_SCOPE` | valid key, not issued for this drop | no | | `AUTH_UNAVAILABLE` | the key check could not complete | yes, backoff | Also frozen, and emitted by the shared mint handlers: `COLLECTION_CLOSED` · `NFTS_LOCKED` · `SESSION_EXPIRED` · `TX_MODIFIED`. For single mints, a transaction failure is exposed as a thrown error code. Batch mint outcomes use `results[].errorCode`; inspect those returned results as well as thrown errors. Use server submission for all v1 mints. The following codes require changing the request rather than resubmitting it: - `CLIENT_BROADCAST_NOT_ALLOWED` — the request carried `transactionHash` (or `txHash` / `transaction_hash`). Sign the transaction `prepare-mint` returned and send it back as `signedTransaction`; we submit it and confirm it. - `CHAIN_NOT_SUPPORTED_BY_SURFACE` — the drop is on a chain this API does not serve. v1 is Solana-only. This is distinct from `CHAIN_DISABLED`, which means a chain was retired platform-wide; your drop's chain is fine, it is just out of scope here. Use `mintUrl` from the collection response to send the collector to GraveMint instead. Frozen but not emitted on the v1 path: `CHAIN_DISABLED` · `UNSUPPORTED_BY_SURFACE` (not emitted anywhere yet), and `NOT_ELIGIBLE` · `WALLET_MISMATCH` (emitted elsewhere in GraveMint, on the cross-chain and mint-page-token paths — neither of which v1 mounts). They will not be renamed. ### The eligibility endpoint `gm.v1.eligibility()` reuses the first-party handler, so it has its own two codes. Neither is frozen: | Code | Meaning | Retry? | |---|---|---| | `INVALID_ADDRESS` | the wallet address is not valid for this chain | no — fix the call | | `CHECK_BUSY` | the eligibility engine is at its global budget | yes, short backoff | ### Rate limiting and suspension | Code | Meaning | Retry? | |---|---|---| | `RATE_LIMITED` | too many requests for this key | yes, honour `Retry-After` | | `COLLECTION_RATE_LIMITED` | too many prepares against this collection, from everyone | yes, honour `retryAfter` | | `WALLET_BLOCKED` | this wallet is suspended | no — on `prepare-mint` the body also carries `reason` / `blockedUntil` / `permanent`; on `execute-mint` it is the code and message only | | `WALLET_HELD` | a wallet this request is made by or for — the signed-in wallet, a minting or recipient wallet named in the body, or the wallet behind the mint session — is on a security hold (reported compromised) | no — show the message; the collector must contact SolanaDeads | | `WALLET_HOLD_UNVERIFIED` | holds are active and we could not confirm the wallet behind this request (typically: a mint session's wallet could not be read), so we refused rather than guess (HTTP 503) | yes, backoff | | `ASSET_BANNED` | this collection or token is on the platform ban list | no | ### Bot protection and the server's time budget Every v1 request passes the platform's bot protection and a per-request time budget. None of these is fixed by retrying the same request immediately: | Code | Meaning | Retry? | |---|---|---| | `BOT_BLOCKED` | this client was blocked by bot protection | no | | `BOTNET_DETECTED` | the request's signature (e.g. its User-Agent) matches known automation tooling | no — from a server, send a descriptive `User-Agent` | | `WALLET_BOT_BLOCKED` | the wallet in this request is restricted by bot protection | no | | `BOT_DETECTED` | the request scored as automated | no — back off | | `REQUEST_TIMEOUT` (HTTP 408) | the server's time budget ran out — the work may still finish | reads: yes, backoff. `execute-mint`: never — `mint()` reports it as `OUTCOME_UNKNOWN` | ### Key class and scope A key carries a class (live or sandbox) and a list of scopes. These are refusals of the key for *this* request, not of the drop, so none of them is fixed by retrying: | Code | Meaning | Retry? | |---|---|---| | `SCOPE_NOT_GRANTED` | the key is valid but was not issued the scope this call needs (e.g. `mint`) | no — ask us to add the scope | | `SANDBOX_KEY_ON_MAINNET` | a sandbox (`gm_test_`) key was used against a mainnet drop | no — use a production key: `gm_pub_` in a browser, `gm_live_` on a server | | `LIVE_KEY_ON_TESTNET` | a production key (`gm_pub_` or `gm_live_`) was used against a test-network drop | no — use your sandbox (`gm_test_`) key | These codes distinguish test-network and production credentials. `TX_MODIFIED` means the signed transaction differs from the prepared transaction. Check the adapter for changed instructions or accounts, then prepare a new transaction. Do not resend the modified bytes. `ORIGIN_NOT_ALLOWED` requires origin registration; `API_KEY_REQUIRED` indicates a missing credential. The SDK retries `429`/`5xx`/network automatically — except `execute`, which it never replays (see "Minting") — with exponential backoff, honouring `Retry-After`, up to `maxRetries`. It does not auto-retry the terminal codes above. ## Rate limits | Limit | Value | Scope | |---|---|---| | Reads on `/v1` (shared with `/gravemint/public`) | 600 / minute | per IP | | `/v1/prepare-mint` and `/v1/execute-mint` (shared bucket) | 60 / minute | per key | | `prepare-mint`, per COLLECTION | 30 / 10s | per collection, shared with first-party traffic | | `validateClaimCode` (claim-code check) | 20 / minute | per IP, shared with first-party code checks | | SDK client-side limiter | 5 rps sustained, 10 burst | your process | Account for each limit when sizing request queues. A shared limit can apply before a per-key limit is reached. Two sharing details that matter in practice: - `prepare` and `execute` share the same 60/min bucket, and a mint needs one of each — so the sustained ceiling is roughly 30 mints per minute per key, not 60. - The read limiter is the same instance `/gravemint/public` uses, so v1 reads and first-party public reads from one IP draw on one 600/min bucket. - The per-collection prepare cap is shared with gravemint.io. It is keyed on the collection alone, so on a hot drop your prepares compete with every first-party collector's — and on a busy launch it can bind well below the 60/min-per-key figure. Treat 60/min as a ceiling you may not reach, not a reservation. The mint limit is keyed on the API key, not the IP, so a partner proxying every collector through one server is not sharing a bucket with the rest of the internet — but it does mean all of that partner's collectors share it. - The claim-code check is per IP, not per key. Validate from the collector's browser. A partner that validates from its own server (a `gm_live_` key) sends every collector's check from one address, so all of its users share 20 a minute. On a `429` the SDK honours `Retry-After` — or, when a limiter sends it only in the body, `retryAfter` — and retries up to `maxRetries`. The server body is on the thrown error as `err.details`. Disable the client-side limiter with `rateLimit: null` only if you have your own queue. Contact us if your integration needs a higher limit. --- ## Versioning `v1` is additive-only. Within the major we will not remove a field, change a field's type, rename an error code, or tighten validation. New behaviour arrives as new fields or a new version. Unversioned legacy paths keep working. Pin the package version in your application and use capability checks to handle collections that require newer or additional features. ```ts await gm.v1.version(); // { version: "v1", stability: "additive-only", docs } ``` `x-gm-client` identifies the SDK version on each request, including browser requests. Deprecations will be announced with a migration path and a real timeline, and old majors keep working while both are live. --- ## Legacy read API The original read-only surface is unchanged and still supported: | Resource | Methods | |---|---| | `gm.discovery` | `getFeatured`, `getLiveMints`, `getTrending`, `getUpcoming`, `getRecentSoldouts`, `listCollections`, `searchCollections` | | `gm.collections` | `get`, `getGallery`, `getRecentlyMinted` | | `gm.nfts` | `get` | | `gm.bounty` | `getSummary`, `getPrizes` | | `gm.platform` | `getCapabilities`, `getTokenPrices` | | `gm.codes` | `validate` (read-only — does not redeem) | These legacy resources use the first-party public API and are not available to partner keys. Use the scoped `gm.v1` methods for partner collection reads and `gm.mint` for minting. Cross-collection discovery remains first-party. The v1 equivalents for collection data include `collection`, `eligibility`, `gallery`, `recentlyMinted`, `bounty`, `bountyPrizes` and `walletMints`. Use `gm.v1.collection()` for display prices and capability checks. --- ## Security notes for partners - Register the exact origins used by your browser application, including staging and previews. - Keep `gm_live_` keys on your server and request revocation if a key is exposed. - Use the API's resolved pricing and eligibility results. - Return signed transactions to GraveMint for submission. --- ## Development ```bash npm test # unit tests, no network npm run typecheck npm run build # dual ESM + CJS ``` ## License MIT --- Source: https://docs.deads.io/sdk/gravemint Markdown: https://docs.deads.io/sdk/gravemint.md --- DOCUMENT: https://docs.deads.io/tools/mcp.md # MCP server The [MCP](https://modelcontextprotocol.io) server gives coding agents access to SDK references, collection data and integration checks. Use it with an MCP-compatible editor or client while building with the GraveMint and GraveMarket SDKs. ## Install This guide covers **@solanadeads/mcp 0.3.1**, with Node.js **20 or later**, GraveMint SDK **^1.3.0** and GraveMarket SDK **0.3.1**. The configurations below launch the published package locally over **stdio**. To use the same server without installing anything, [connect to the hosted server](#connect-remotely) instead. ```jsonc [Claude Desktop] // claude_desktop_config.json { "mcpServers": { "solanadeads": { "command": "npx", "args": ["-y", "@solanadeads/mcp"], "env": { "GRAVEMINT_API_KEY": "gm_live_..." } } } } ``` ```bash [Claude Code] claude mcp add solanadeads \ --env GRAVEMINT_API_KEY=gm_live_... \ -- npx -y @solanadeads/mcp ``` ```jsonc [Cursor] // .cursor/mcp.json { "mcpServers": { "solanadeads": { "command": "npx", "args": ["-y", "@solanadeads/mcp"], "env": { "GRAVEMINT_API_KEY": "gm_live_..." } } } } ``` GraveMarket's tools need no credential. GraveMint's are invite-only — without a key the server still runs and simply exposes fewer tools, so you can try it before you have one. Use a server-side `gm_live_` key for GraveMint tools. Browser keys require an Origin header and are not suitable for MCP. See [keys and origins](https://docs.deads.io/guides/keys.md). ### Connect remotely The same server runs at **`https://mcp.deads.io/mcp`** over Streamable HTTP. Point any client that supports remote MCP servers at it: ```jsonc [Claude Desktop / Cursor] { "mcpServers": { "solanadeads": { "url": "https://mcp.deads.io/mcp", "headers": { "X-GraveMint-Api-Key": "gm_live_..." } } } } ``` ```bash [Claude Code] claude mcp add --transport http solanadeads https://mcp.deads.io/mcp \ --header "X-GraveMint-Api-Key: gm_live_..." ``` The key is optional and is **yours**. Send it with each request, in `X-GraveMint-Api-Key` or as `Authorization: Bearer gm_live_...`, and that request runs with your key alone. Nothing is stored between requests. Without a key you get the GraveMarket tools, `sdk_reference` and `review_integration`. Each request carries one JSON-RPC message, and requests are rate limited per caller. > **WARNING: Keep server keys private** The hosted server accepts requests from any origin, so a web page could call it — but a key in a page's JavaScript is a key every visitor can copy. Connect from your MCP client, your editor or your own backend, where the key stays private. ## Integration checks {#why-you-want-it} `review_integration` checks supplied code for known integration mistakes and returns line numbers and suggested changes. Its checks include price-display handling, client-side fee calculations, server submission, partial batch results and wallet-mint counts. Ask your coding agent: > Review this file for Solana Deads integration mistakes. Use the result alongside your application's type checks and tests. The review is a diagnostic aid, not a guarantee that an integration is complete. ## What changed in 0.3.0 - **Full type context.** `sdk_reference` includes the response/input types and error classes, so an agent can inspect fields as well as method signatures. - **Paginated mint history.** `gravemint_get_all_minted` accepts `limit`, `offset`, `sort` and `search`. Read successive pages until `hasMore` is false; use `newest` or `oldest` for pagination because `popular` ranks within a page. - **Supply semantics.** Availability includes `burned`; `available` can be `null` for an open-ended generative drop. In-progress reservations can still count as available, so this count alone cannot establish that a drop is sold out. - **Larger integration reviews.** `review_integration` accepts up to 200,000 characters and refuses larger inputs. It also flags client-side platform-fee arithmetic. Its checks cover known mistakes; a clean result does not prove the integration is correct. Match these contracts to your installed SDK and target API deployment. Publishing a package does not deploy its server dependencies. See [version and deployment compatibility](https://docs.deads.io/guides/overview.md#version-and-deployment-compatibility). For GraveMint SDK 1.3.0, an uncertain outcome after submission can throw `MintOutcomeUnknownError` with `code: "OUTCOME_UNKNOWN"` and `sessionIds`. **Do not automatically mint again:** the first mint may still complete. See [the SDK mint flow](https://docs.deads.io/sdk/gravemint.md#minting) for the full handling contract. ## Tools ### Always available | Tool | What it does | |---|---| | `sdk_reference` | Published SDK method signatures, response types and error classes. | | `review_integration` | The checks above, with line numbers and concrete fixes. | ### GraveMarket — no credential needed `gravemarket_get_collection` · `gravemarket_search` · `gravemarket_get_collection_activity` · `gravemarket_get_collection_stats` ### GraveMint — needs `GRAVEMINT_API_KEY` **The drop** `gravemint_get_collection` · `gravemint_check_eligibility` · `gravemint_get_wallet_mints` · `gravemint_get_availability` **Its art and history** `gravemint_get_gallery` · `gravemint_get_recently_minted` · `gravemint_get_all_minted` · `gravemint_get_traits` · `gravemint_get_leaderboards` `gravemint_get_gallery` lists the NFTs that can still be minted; `gravemint_get_all_minted` lists the minted ones and pages with `limit`, `offset`, `sort` and `search`. **Pricing** `gravemint_get_token_prices` · `gravemint_get_phase_price` **Claim codes** `gravemint_get_claim_codes` · `gravemint_get_claim_code_benefits` · `gravemint_validate_claim_code` *(read-only — it does not redeem or consume a use)* **Bounty** `gravemint_get_bounty` Reads are restricted to the collections assigned to your key. Requests outside that scope return `403 COLLECTION_NOT_IN_SCOPE`. ## Read-only tools {#what-it-deliberately-cannot-do} The server exposes documentation and read tools. It does not expose wallet signing, `prepare-mint` or `execute-mint`. Preparation reserves supply, so use the SDK reference to inspect its response type and implement the mint flow in your application. --- Source: https://docs.deads.io/tools/mcp Markdown: https://docs.deads.io/tools/mcp.md