CollectorCrypt API
Bridge physical trading cards and blockchain — buy, sell, and trade collectible card NFTs on Solana, open mystery gacha packs, redeem NFTs for real cards, and swap assets peer-to-peer.
Marketplace Base URL: https://api.collectorcrypt.com
Marketplace Devnet URL: https://dev-api.collectorcrypt.com
Gacha Base URL: https://gacha.collectorcrypt.com
Gacha Devnet URL: https://dev-gacha.collectorcrypt.com
Key Concepts
Product Suite
- Marketplace — buy/sell/trade collectible card NFTs using USDC on Solana
- Gacha Machine — mystery pack system; spend USDC for randomized Pokemon card NFTs with VRF-verified fairness
- Vault — bridge NFTs ↔ physical cards; deposit real cards to mint NFTs, burn NFTs to ship physical cards
- Swap — peer-to-peer on-chain asset swaps via escrow (NFTs, pNFTs, cNFTs, SPL tokens)
- Launchpads — drop pages for minting new collectible releases
- Bidder — eBay bid automation; deposit funds, service bids and tokenizes won cards
Solana Programs
| Program | ID |
|---|
| Marketplace | CcmRKTuZCGJBWQwMHvDYApBRvSZNHqGJXkznqpDTSQUr
|
| Swap | CCSwaptcDXtfjyRMYBqavqBwiW162yXFAJrXN53UuENC
|
NFT Types Supported
- Programmable NFTs (pNFTs) — Metaplex standard with enforced royalties; use
- Compressed NFTs (cNFTs) — Metaplex Bubblegum merkle-tree structure; use
- Metaplex Core () — single-account standard; many live listings are Core (browse responses return ). For Core listings, omit from buy/list/offer bodies — do NOT pass /. Only send when the asset is actually pNFT or cNFT.
Currency
All marketplace and gacha transactions use USDC (SPL Token) on Solana. Shipping fees accept USDC or USDT. Gas fees paid in SOL.
Marketplace prices are in whole USDC (e.g.,
means $25.00 USDC).
Transaction Pattern (Marketplace & Shipping)
Three-step flow:
- Build — POST to API endpoint, receive base64-encoded unsigned transaction(s)
- Sign — Sign with the Solana wallet (Phantom/Solflare, or the Starchild wallet helper — see below)
- Broadcast — POST signed transaction to
HTTP status — accept BOTH and as success. Verified live:
and
return
, not
. Treat any 2xx as success; do not hard-check
.
Response body may be raw base64 text, NOT JSON. with
can return the unsigned transaction as a plain-text body. Parse defensively:
python
text = response.text
try:
data = response.json()
tx = data.get("transaction") or data.get("serializedTransaction")
except Exception:
tx = text # body was the raw base64 transaction
Starchild wallet signing returns snake_case . When signing with the platform wallet:
python
from core.skill_tools import wallet
signed = wallet.wallet_sol_sign_transaction(unsigned_tx)
# → {"signed_transaction": "<base64>", "encoding": "base64"}
signed_tx = signed.get("signed_transaction") or signed.get("signedTransaction")
Look for
first, then the camelCase variant. Feed
into
as the
field.
Gacha Rarity Tiers
| Rarity | Probability |
|---|
| Epic | 1% |
| Rare (High) | 4% |
| Uncommon (Mid) | 15% |
| Common (Low) | 80% |
VRF (Verifiable Random Function)
Gacha rolls use a Verifiable Random Function for tamper-proof, provably-fair results. Selection is two-stage:
- Roll → tier: a uniform integer roll in the range 1–100,000,000, seeded by the player's own wallet signature on the open transaction, maps to a rarity tier via the machine's configured weights (see in ).
- Card → within tier: a provable selection picks the specific NFT inside that tier.
The seed (wallet signature) and result are committed to Solana in the award transaction's on-chain memo, preventing prediction or manipulation. Verify independently:
GET /api/vrf/verify?memo=YOUR_MEMO
— returns the proof, public key, transaction signature, and whether the stored roll matches the recomputed roll.
/verify-selection/YOUR_MEMO
— browser-side replay of the card selection against the on-chain memo.
The docs do not name a specific VRF algorithm; don't assert one.
How to Call
Gacha API — No API Key (the key does not exist)
There is no API key. Do not set , do not send an header, and do not try to obtain a key — the CollectorCrypt team has confirmed no key system exists. Every gacha endpoint, reads and writes alike, is called with no authentication.
bash
# Reads AND writes — no auth header of any kind
curl -s "https://gacha.collectorcrypt.com/api/machines"
curl -s -X POST "https://gacha.collectorcrypt.com/api/generatePack" \
-H "Content-Type: application/json" \
-d '{"playerAddress":"WALLET_PUBKEY"}'
Ignore the docs' authentication section. The public docs say
"Each request must include a valid header" and reference a partner "slug." That does not reflect reality — confirmed both by the team (no key system exists) and by live testing (
returns
with no header). There is nothing to request and no header to add.
Without a key, generated transactions carry a
memo. Security comes from the
wallet signature, not from any API credential — nothing executes until the user signs the transaction this API hands back.
Reference implementation:
Gacha Starter Demo —
https://github.com/daxherrera/gacha-starter (full pack → sign → submit → open flow). Try every call live on devnet at
https://dev-gacha.collectorcrypt.com
(fund a devnet wallet via the USDC faucet:
https://spl-token-faucet.com/?token-name=USDC-Dev).
Marketplace API — No Auth for Reads
Public GET endpoints (browse listings) need no authentication. Write endpoints return unsigned transactions for wallet signing — no API key involved.
bash
# Browse listings — no auth, 5s cache for unauthenticated requests
curl -s "https://api.collectorcrypt.com/marketplace?page=1&step=20&categories=Pokemon"
# Create listing — returns base64 unsigned transaction
curl -s -X POST "https://api.collectorcrypt.com/marketplace/list" \
-H "Content-Type: application/json" \
-d '{
"wallet": "YOUR_SOLANA_ADDRESS",
"cardId": "CC_CARD_DB_ID",
"nftAddress": "NFT_MINT_ADDRESS",
"currency": "USDC",
"price": 50
}'
Shipping API — Two Auth Tracks
Track A — Privy Identity Token:
Authorization: Bearer <privy-identity-token>
Track B — Sign-In With Solana (SIWS):
- — get nonce + message to sign
- Sign message with wallet (ed25519, base58)
- — exchange signature for access/refresh tokens
bash
# Step 1: Get nonce
curl -s -X POST "https://api.collectorcrypt.com/auth/wallet/nonce" \
-H "Content-Type: application/json" \
-d '{"wallet": "YOUR_PUBKEY", "partnerAppId": "your-slug", "domain": "your-app.com", "uri": "https://your-app.com"}'
# Step 3: Verify and get tokens
curl -s -X POST "https://api.collectorcrypt.com/auth/wallet/verify" \
-H "Content-Type: application/json" \
-d '{"message": "<message-from-nonce>", "signature": "<base58-ed25519-signature>"}'
# Returns: {"accessToken": "cca_...", "refreshToken": "ccr_...", "expiresAt": ...}
Token management:
- Access token lifetime: 15 minutes
- Refresh:
POST /auth/wallet/refresh
with
- Logout:
- Track B limitation: crypto payment only (no card payments), Solana signatures only
Intent Routing
Marketplace
Base:
https://api.collectorcrypt.com
All POST endpoints return base64-encoded unsigned Solana transaction(s). Sign with wallet, then broadcast via
.
List an NFT —
| Field | Type | Required | Description |
|---|
| string | yes | Seller's Solana address |
| string | yes | CollectorCrypt card DB ID |
| string | yes | NFT mint address |
| string | yes | |
| number | yes | Listing price in USDC |
| enum | no | or |
| boolean | no | If , backend pays fees |
Errors:
(on-chain error),
(already listed)
Cancel a Listing — POST /marketplace/cancel-listing
| Field | Type | Required | Description |
|---|
| string | yes | Caller's wallet |
| string | yes | NFT mint address |
| string | no | Defaults to |
Errors:
(on-chain error),
(no listing found)
Update a Listing Price — POST /marketplace/update-listing
| Field | Type | Required | Description |
|---|
| string | yes | Seller's wallet |
| string | yes | NFT mint address |
| number | yes | Updated price in USDC |
| string | yes | |
Errors:
(price unchanged or on-chain error),
(no listing found)
Buy an NFT —
| Field | Type | Required | Description |
|---|
| string | yes | Buyer's wallet |
| string | yes | NFT mint address |
| number | yes | Listed price in USDC |
| string | yes | |
| enum | no | or — omit for Metaplex Core listings () |
| string | no | Resolved from DB if omitted |
| enum | no | (default) or |
Success status is OR (live buys return
).
Response body may be raw base64 text rather than JSON — parse defensively (see Transaction Pattern above).
- → single base64 unsigned transaction (JSON or plain base64 text)
- →
{"transactions": ["BASE64_WITHDRAW_TX", "BASE64_BUY_TX"], "fundingSource": "escrow"}
Escrow requires minimum
USDC balance.
Errors:
(on-chain error, insufficient escrow),
(card/listing not found),
(owner changed)
End-to-End Buy Recipe (tested live)
python
import requests
from core.skill_tools import wallet
BASE = "https://api.collectorcrypt.com"
buyer = "YOUR_WALLET_PUBKEY"
# 1. Find a listed item, cheapest first
listings = requests.get(f"{BASE}/marketplace",
params={"page": 1, "step": 1, "marketplaceStatus": "Buy now", "orderBy": "listedPriceAsc"}
).json()["filterNFtCard"]
item = listings[0]
mint = item["nftAddress"]
price = float(item["listing"]["price"])
seller = item["owner"]["wallet"]
std = item.get("nftStandard") # "core" | "Pnft" | "Cnft"
# 2. Build buy tx. Omit tokenStandard for Core listings.
body = {"wallet": buyer, "nftAddress": mint, "price": price,
"currency": "USDC", "sellerWallet": seller}
if std in ("Pnft", "Cnft"):
body["tokenStandard"] = std
r = requests.post(f"{BASE}/marketplace/buy", json=body)
assert r.status_code in (200, 201) # buy returns 201
try: unsigned = r.json().get("transaction") or r.json().get("serializedTransaction")
except Exception: unsigned = r.text # may be raw base64
# 3. Sign with Starchild wallet (snake_case key)
signed = wallet.wallet_sol_sign_transaction(unsigned)
signed_tx = signed.get("signed_transaction") or signed.get("signedTransaction")
# 4. Broadcast
b = requests.post(f"{BASE}/marketplace/broadcast",
json={"wallet": buyer, "signedTransaction": signed_tx, "nftAddress": mint})
assert b.status_code in (200, 201) and b.json().get("success") # broadcast returns 201
sig = b.json()["signature"]
# 5. Verify on Solana RPC — the SOURCE OF TRUTH (see below)
status = requests.post("https://api.mainnet-beta.solana.com", json={
"jsonrpc": "2.0", "id": 1, "method": "getSignatureStatuses",
"params": [[sig], {"searchTransactionHistory": True}]}
).json()["result"]["value"][0]
assert status and status["err"] is None and status["confirmationStatus"] == "finalized"
Verify via Solana RPC signature status, not a marketplace refetch. Marketplace metadata refresh lags after a buy, and there is no
lookup filter (
is fuzzy and may not reflect the new owner immediately). The authoritative post-broadcast check is
on the broadcast
:
json
{"confirmationStatus": "finalized", "err": null, "status": {"Ok": null}}
(and
of
/
) means the buy settled on-chain. Only fall back to a marketplace refetch for display, never as the success gate.
Regression fixture — a real buy that succeeded (use to smoke-test the flow):
| Field | Value |
|---|
| Item | 2017 #024 POKE BALL PSA 9 — Pokémon Japanese Sun & Moon, Ash vs. Team Rocket Deck Kit |
| Price | 19 USDC |
| NFT mint | 4AWreh7pqVb4jhANAXFz2fwSPKqWr5ZAFMgaAFWZS9nk
|
| Seller | FfveJgtFmGPCSazkjicf21WLk9rdUaMsownVVA8LRhwf
|
| Broadcast signature | 5SwxeuWGjWbiAFq8YVN3Qsw1PXnKXBakKA2ykgccc78gg6JkGnyi48StezcijXmgWHyGWvojq8TYmTXJoC9XnbjH
|
| Solana status | , |
Make an Offer — POST /marketplace/make-offer
| Field | Type | Required | Description |
|---|
| string | yes | Buyer's wallet |
| string | yes | CollectorCrypt card DB ID |
| string | yes | NFT mint address |
| number | yes | Offer price in USDC |
| string | yes | |
| enum | no | or |
| boolean | no | Backend fee payer if |
| string | no | Current NFT owner |
| string | no | cNFT collection hash |
Errors:
(can't determine owner or on-chain error)
Cancel an Offer — POST /marketplace/cancel-offer
| Field | Type | Required | Description |
|---|
| string | yes | Buyer's wallet |
| string | yes | NFT mint address |
| boolean | no | If , USDC stays in escrow; default |
Backend auto-sets
if wallet has insufficient SOL for rent.
Errors:
(no active offer found)
Update an Offer — POST /marketplace/update-offer
| Field | Type | Required | Description |
|---|
| string | yes | Buyer's wallet (signer) |
| string | yes | NFT mint address |
| string | yes | |
| string | yes | Buyer's wallet (typically = ) |
| number | yes | New offer price in USDC |
Important: Escrow must already contain the full new amount — this instruction does NOT transfer additional USDC.
Errors:
(price unchanged, no escrow, insufficient balance, on-chain error),
(offer not found)
Accept an Offer — POST /marketplace/accept-offer
| Field | Type | Required | Description |
|---|
| string | yes | Seller's wallet (NFT owner) |
| string | yes | NFT mint address |
| string | yes | Buyer's wallet |
| string | yes | |
| number | yes | Offer price in USDC |
| enum | no | or |
| boolean | no | Backend fee payer if |
cNFT edge case: If the cNFT is currently listed, returns:
json
{
"requiresCancelListing": true,
"cancelListingTx": "BASE64_CANCEL_LISTING_TX",
"message": "This cNFT is currently listed. Please sign..."
}
Sign the cancel-listing tx first, then re-call accept-offer.
Errors:
(price drift, on-chain error),
(offer not found)
Browse Listings —
Public, no auth required. 5-second cache for unauthenticated requests.
Pagination:
| Param | Type | Default | Description |
|---|
| integer | 1 | 1-indexed page |
| integer | 100 | Page size (max 100) |
Filters (all optional):
| Param | Type | Description |
|---|
| string | Substring match against itemName, nftAddress, gemrateCardName, gradingID. This is the only way to look up by mint — there is NO query param (GET /marketplace?nftAddress=<mint>
returns ). Use instead. |
| string | Filter by owner wallet |
| boolean | Hide caller's own cards (requires auth) |
| boolean | Only favorited cards (requires auth) |
| boolean | Only from followed users (requires auth) |
| string | Cards owned by users that this wallet follows |
| CSV | , |
| CSV | , |
| CSV | Global tag names (e.g., , ) |
| / | number | USDC price range |
| / | number | Insured value range |
| / | integer | Card year range |
| CSV | , , , , , , , , , etc. |
| CSV | , , , , , , |
| CSV | , others |
| boolean | Only authenticated cards |
| boolean | Only autographed cards |
| boolean | Include burned cards (default excludes) |
| string | Substring match |
| / | number | Numeric grade range |
| CSV | , , , , , , , , , , , , |
| / / / | string | Substring match for sealed/comic metadata |
| string | Word-boundary match against gemrateCardName |
| boolean | Only cards with active CC buyback |
| / / / / | string | Gemrate-specific filters |
Sort — values:
,
,
,
,
,
(insured value),
,
,
,
,
,
Default:
Response:
json
{
"filterNFtCard": [
{
"id": "card_db_id",
"itemName": "...",
"nftAddress": "NFT_MINT_ADDRESS",
"nftStandard": "Pnft",
"blockchain": "Solana",
"category": "Pokemon",
"type": "Card",
"year": 1999,
"grade": "10",
"gradeNum": 10,
"gradingCompany": "PSA",
"gradingID": "...",
"insuredValue": "...",
"listing": {
"price": "25",
"currency": "USDC",
"marketplace": "CC",
"sellerId": "...",
"createdAt": "ISO_DATE"
},
"offers": [{"id": "offer_id"}],
"owner": {
"id": "user_id",
"name": "...",
"wallet": "OWNER_WALLET_ADDRESS"
},
"images": {
"front": "URL",
"frontM": "URL",
"frontS": "URL",
"back": "URL",
"backM": "URL",
"backS": "URL"
}
}
],
"findTotal": 1234,
"total": 50000,
"totalPages": 25,
"cardsQtyByCategory": {"Pokemon": 800, "Baseball": 200}
}
- is when unlisted
- is array of active offer IDs
- = total matching filter; = total marketplace cards overall
- = per-category count (excludes filter)
Broadcast a Transaction — POST /marketplace/broadcast
| Field | Type | Required | Description |
|---|
| string | yes | Signer wallet address |
| string | yes | Base64-encoded signed transaction |
| string | no | Triggers metadata refresh after 5s |
Success status is OR (live broadcasts return
).
Response:
json
{
"success": true,
"signature": "SOLANA_TRANSACTION_SIGNATURE",
"message": "Transaction broadcast successfully"
}
Don't treat the
as a failure — check
/ presence of
, not the status code.
Insufficient funds recovery: If broadcast returns
with
"code": "INSUFFICIENT_FUNDS_RETRYABLE"
, rebuild the original transaction with
so the backend pays fees, then re-sign and re-broadcast.
Validation: Transaction is validated against an allow-list of program IDs before submission.
Errors:
(invalid transaction, insufficient funds retryable, submission failure),
(program ID not on allow-list)
Gacha Machine
Base:
https://gacha.collectorcrypt.com
| Method | Endpoint | Primary Params | Description |
|---|
| GET | | — | Machine operational status (open/closed) |
| GET | | — | NFT inventory counts by rarity per machine |
| GET | | — | Full config for all machines: pricing, , (roll→tier bounds), live , %, and (rarity-weighted expected insured value) |
| GET | | , , , | Available NFTs with filtering |
| POST | | Body: , , , | Create purchase transaction for one pack |
| POST | | Body: (1-100), , , , | Batch purchase multiple packs |
| POST | | Body: | Open purchased pack, receive NFT |
| POST | | Body: , , | Sell NFT back for USDC (within 72 hours) |
| GET | | | Check buyback completion status |
| GET | | , | Pre-check NFT buyback eligibility + refund (before building a tx) |
| GET | | | Full pack lifecycle: purchase, NFT send (incl. , , ), and buyback history |
| POST | | Body: (base64) | Submit signed transaction to Solana |
| POST | | Body: , , , | Gift packs to another wallet |
| GET | | | Gift history (sent/received) |
| GET | | | Pack counts by type (purchased vs gifted, used vs unused) |
| POST | /api/generatePurchasedPack
| Body: , , | Create sign challenge for opening gifted/purchased packs |
| POST | | Body: , , , /, | Consume purchased pack after signing, returns memo |
| GET | | , , , , | Recent winners with optional filtering |
| GET | | | Verify VRF proof for a roll |
Gacha Response Shapes
generatePack response:
json
{"memo": "slug-uuid", "transaction": "base64_encoded_transaction"}
The
is
(there is no API key, so no partner slug prefix applies). Body is
{playerAddress, packType?, turbo?, altPlayerAddress?}
—
defaults to
(use
for legendary).
openPack response (normal win):
json
{
"success": true,
"transactionSignature": "...",
"nft_address": "...",
"nftWon": {
"content": {
"metadata": {"name": "...", "description": "...", "attributes": []}
}
},
"points": 0,
"roll": 12345678,
"rarity": "Epic|Rare|Uncommon|Common"
}
is an integer in the range 1–100,000,000 (NOT a fraction).
is nested under
— there is no top-level
/
.
openPack response (turbo-mode buyback, Common roll): adds
"code": "TURBO_MODE_BUYBACK"
and
"buybackAmount": 42500000
(USDC base units) — the Common NFT is auto-sold instead of delivered.
openPack response (payment not yet confirmed):
json
{"success": true, "code": "WAITING_FOR_WEBHOOK", "memo": "slug-uuid-string"}
Do NOT assume an NFT was delivered on this response — poll
GET /api/pack/status?memo=...
(or retry
) until the win resolves.
openPack is idempotent: calling it again on an already-opened pack returns the original result (same
,
,
,
) — safe to retry.
Prize tier mapping (
and
return numeric
;
returns human-readable
):
= Epic,
= Rare,
= Uncommon,
= Common.
buyback response:
json
{
"serializedTransaction": "base64",
"refundAmount": 42500000,
"memo": "..."
}
Shipping / Vault Redemption
Base:
https://api.collectorcrypt.com
| Method | Endpoint | Primary Params | Description |
|---|
| POST | | Body: , | Fee estimate without creating shipment; returns , , , , , , |
| POST | | Body: , , (|), , , | Create pending shipment, returns unsigned transactions |
| POST | /blockchain/:outboundShipmentId/burn
| Body: (base64 signed), | Broadcast signed burn transactions; returns , , |
| GET | | — | List user shipments |
| GET | | | Get specific shipment status |
Shipping Address Management
| Method | Endpoint | Description |
|---|
| GET | | List saved addresses |
| GET | | Get specific address |
| POST | | Create new address |
| PATCH | /shipping-address/update/:id
| Update address |
| DELETE | | Delete address |
Shipping Rates
| Region | Base | Each Additional | Signature | Pack Surcharge |
|---|
| USA | $5.99 | $3.00 | +$3.00 (value >= $500) | $0.18/pack |
| Canada | $20.99 | $3.00 | Included | $0.25/pack |
| Europe | $29.99 | $3.00 | Included | $0.25/pack |
| Australia/NZ | $34.99 | $3.00 | Included | $0.25/pack |
| Rest of World | $34.99 | $3.00 | Included | $0.25/pack |
Insurance auto-applies at 0.5% of declared value when package exceeds $5,000.
Redeem/Prepare Response Shape
json
{
"outboundShipmentId": "shipment_...",
"transactions": ["<base64-unsigned-tx>"],
"evmTransactions": [],
"totalCost": 14.99,
"submitUrl": "/blockchain/shipment_.../burn",
"breakdown": {
"region": "USA",
"declaredValue": 700,
"numberOfCards": 10,
"lines": [
{"code": "shipping_base", "label": "Base shipping (USA, first card)", "amount": 5.99, "unitPrice": 5.99}
]
}
}
Auth (SIWS — Sign-In With Solana)
Base:
https://api.collectorcrypt.com
| Method | Endpoint | Primary Params | Description |
|---|
| POST | | Body: , , , | Get nonce + EIP-4361 signing message |
| POST | | Body: , (base58 ed25519) | Exchange signature for access + refresh tokens |
| POST | | Body: | Refresh expired access token |
| POST | | Body: (+ access token in header) | Invalidate tokens |
Swap Program (On-Chain)
Program ID:
CCSwaptcDXtfjyRMYBqavqBwiW162yXFAJrXN53UuENC
The swap program is an Anchor-based Solana smart contract. Trades follow this lifecycle:
Open → Approved → Executed → Settled → (Closed)
↑ ↑
cancel_trade cancel_trade
Instructions:
| Instruction | Description |
|---|
| Initialize trade between two wallets |
| Signal readiness (both parties must approve) |
| Commit to execution (both parties must execute) |
| Abort open or approved trades |
| Transfer standard NFTs into/out of escrow |
| Transfer programmable NFTs |
| Transfer compressed NFTs |
| Transfer SPL tokens |
| Backend confirmation of completion |
| Final cleanup and rent refund |
Marketplace On-Chain Details
Account Structure
| Account | Purpose |
|---|
| Market | Singleton PDA — config (admin keys, fees, treasury, paused status) |
| Listing | Sale info indexed by asset ID + seller, with delegation authority |
| Offer | Buyer offer details with independent escrow backing |
| UserEscrow | Per-user USDC deposit tracking for offer collateral |
Fee Structure
Platform fee: 200 basis points (2%). Configurable 0–200 bps.
fee = floor(price * platform_fee_bps / 10000)
Uses u128 intermediate precision to prevent overflow.
Security Properties
- Non-custodial listings — NFTs remain in seller wallets using delegation, not escrow transfers
- Frontrun prevention — parameters validate on-chain prices match expectations
- Self-trade blocking — enforced validation
- PDA determinism — fixed seed schemes prevent unauthorized account substitution
- Broadcast allow-list — transactions validated against approved program IDs before submission
Error Handling
Success is any 2xx — accept AND . Several marketplace write endpoints (
,
) return
on success. Never gate on
; check for a 2xx status plus
/ a
/ a transaction body.
All APIs return errors in a standard shape:
json
{"statusCode": 400, "message": "human-readable error", "error": "Bad Request"}
| Status | Meaning |
|---|
| Invalid input, on-chain failure, price mismatch, insufficient escrow, signing failure |
| Invalid or expired token |
| Access denied, program ID outside broadcast allow-list |
| Card, NFT, listing, offer, or address not found |
| Conflict (already listed, owner changed, wallet linked to Privy account) |
| Rate limit exceeded |
| Machine empty, too many packs open, or processing error |
Safety Notes
- Marketplace prices are in whole USDC. means $25.00 USDC.
- Buy/broadcast succeed with HTTP — accept any 2xx, parse the body which may be raw base64 (not JSON), and verify the result on Solana RPC (), not via a marketplace refetch. See the End-to-End Buy Recipe.
- Metaplex Core () is common — omit for those; only send / when the asset really is one.
- Starchild wallet signing returns (snake_case) — read that (or the camelCase fallback) before broadcasting.
- Gacha is in USDC lamports. 42,500,000 = $42.50 USDC (1 USDC = 1,000,000 lamports).
- Gacha buyback window is 72 hours. After that, the NFT cannot be sold back to the machine.
- Buyback refund is a per-machine percentage of insured/market value (not the purchase price). The rate is exposed as in (e.g. = 85%); don't assume a fixed global rate. Use
GET /api/buyback/available?wallet=…&nft=…
to get the exact refund before building a buyback tx (USDC base units, already adjusted for the machine's buyback % and capped at 40,000 USDC).
- Non-custodial listings — NFTs stay in seller wallets via delegation. If a seller transfers an NFT without canceling the listing, the listing becomes invalid. Backend auto-cancellation is not guaranteed.
- Escrow must be pre-funded for offer updates — does NOT transfer additional USDC; the escrow must already contain the full new amount.
- cNFT accept-offer may require two transactions — if the cNFT is currently listed, the API returns a cancel-listing tx that must be signed first.
- Shipping creates real physical shipments. Always confirm address and NFT selection with the user before calling .
- Signed transactions are irreversible. Verify NFT address, price, and wallet before broadcasting.
- VRF rolls are final. Once a pack is opened, the result cannot be changed or re-rolled.
- Track B (SIWS) is crypto-only. No card payment support. Wallets already linked to Privy accounts will get .
- — use this flag when the user's wallet lacks SOL for transaction fees; the backend will pay gas. Also use it to recover from
INSUFFICIENT_FUNDS_RETRYABLE
broadcast errors.
Known Limitations
- Marketplace uses delegation, not escrow — if a seller transfers their NFT elsewhere without canceling, the listing breaks silently. Always check listing validity before buying.
- Field names differ across endpoints — listing uses , cancel-listing uses , update-listing uses + + . Always check the exact field names per endpoint above.
- Gacha codes are machine-specific (e.g., , ). Use to discover available pack types and current pricing.
- No keyword search for gacha NFTs — use with and filters, or browse via .
- Swap is on-chain only — no REST API wrapper; interact via Solana transactions against the Anchor program IDL.
- Access tokens expire in 15 minutes (Track B / SIWS) — implement refresh token rotation for long sessions.
- eBay Bidder has no public API documentation — it's a consumer-facing tool at
https://bid.collectorcrypt.com
.
- Launchpads are admin-managed drop pages — no public API for creating or configuring them.
- Browse listings cache — unauthenticated GET requests are cached for 5 seconds.