Endpoints we call
Every call, with the request and the response fields we read
Shapes below follow the Collector Crypt gacha API docs,
trimmed to the fields we read. All calls: JSON over HTTPS, flat response objects, an
x-api-key header and a User-Agent header (your WAF must let it through). We time out after
30 seconds. Amounts named *Amount / refund_amount are USDC base units (6 decimals);
price, cost and insured_value are whole USDC.
1. Catalogue
GET /api/v1/machines (key required, 401 without it)
Your machines only, with live state. We sell a machine only when sells is true.
{ "machines": [ {
"code": "partner_pokemon_50", "name": "Elite Pack", "enabled": true,
"price": 50, "instantBuyback": 85, "tierRanges": { },
"partnerFee": 5, "targetEv": 46, "ev": 46.1,
"odds": { "epic": 0.01, "rare": 0.04, "uncommon": 0.15, "common": 0.80 },
"evLow": 44.2, "evHigh": 52.7, "reachable": true,
"stock": { "common": 34, "uncommon": 42, "rare": 86, "epic": 124 },
"low": false, "sells": true
} ] }| Field | Meaning |
|---|---|
code | The packType to buy with |
enabled | Machine toggled on |
partnerFee | Our per-pack fee (see Fees) |
targetEv / ev | EV the machine aims for / will deliver right now |
odds | Per-tier odds a pack bought now rolls; sum to 1 |
evLow, evHigh, reachable | Achievable EV band on current stock, and whether targetEv is inside it |
stock, low | Active cards per tier; a tier below its restock threshold |
sells | enabled and EV in band and stocked. If false, a purchase is refused. |
GET /api/v1/stock returns just the per-tier counts and low, when only availability matters.
GET /api/machines
Full config and live stock for every public machine.
{ "machines": [ {
"code": "pokemon_50", "name": "Elite Pack", "image": "…", "public": true,
"price": 50, "contains": 1, "instantBuyback": 85,
"odds": { "epic": 0.01, "rare": 0.04, "uncommon": 0.15, "common": 0.80 },
"tierRanges": { "common": { "start": 0, "end": 25 }, "epic": { "start": 200, "end": 99999 } },
"stock": { "common": 34, "uncommon": 42, "rare": 86, "epic": 124 },
"ev": 51.2
} ] }instantBuyback is the buyback percentage of insured value. ev is null until first
computed, never 0.
GET /api/status
{ "machineStatus": "running",
"gachas": [ { "code": "pokemon_50", "price": 50, "status": "open", "isOpen": true } ] }machineStatus: "stopped" = emergency stop, nothing can be bought. A machine that is
closed, or missing from the list, is not sold.
GET /api/getNfts?code=&rarity=&limit=&cursor=
The card pool, ordered by insured value. limit ≤ 100; page with the opaque nextCursor
until hasMore is false.
{ "nfts": [ { "nft_address": "…", "name": "…", "rarity": "epic", "image": "…",
"insured_value": 250 } ], "hasMore": true, "nextCursor": "…" }GET /api/getRecentWinners?packType=
Up to 5 recent winners: { "success": true, "data": [ { "winner", "prize_tier", "nft", "timestamp", "insuredValue" } ] }. An unknown pack type returns data: [], not 404.
2. Buy
POST /api/generateYoloPacks
The house wallet buys 1–100 packs; the cards go to the user.
{ "count": 2, "packType": "pokemon_50", "turbo": false,
"playerAddress": "<house wallet>", "altPlayerAddress": "<user wallet>" }{ "yoloId": "uuid", "count": 2, "transactions": [
{ "memo": "slug-uuid-1", "transaction": "<base64>" },
{ "memo": "slug-uuid-2", "transaction": "<base64>" } ] }Each transaction is signed and submitted on its own. Errors: 400 bad body or count, 403
blocked address, 500 machine error with a details cause (off balance, low, off).
POST /api/submitTransaction
{ "signedTransaction": "<base64>" }
→ { "success": true, "signature": "…", "confirmationStatus": "confirmed" }confirmationStatus is confirmed, finalized or submitted (sent, not yet confirmed).
HTTP 200 with success: false is a rejection. Errors: 400 bad transaction, 403 not signed
by the gacha wallet, 500 chain error.
GET /api/getGifted?wallet=
Our cross-check that a purchase was paid, matched by memo.
{ "sent": [ { "id": 1, "memo": "…", "receiver": "…", "pack_type": "pokemon_50", "count": 1,
"cost": 50, "status": "confirmed", "webhook_confirmed": true, "transaction_signature": "…" } ],
"perpack": [ { "memo": "…", "opened_date": "…", "send_nft_txn": "…", "insured_value": 50 } ] }3. Open
POST /api/openPack
{ "memo": "slug-uuid" }
→ { "success": true, "transactionSignature": "…", "nft_address": "…",
"nftWon": { "content": { "metadata": { "name": "…", "attributes": [] } } },
"rarity": "Epic", "roll": 12345678 }rarity:Epic,Rare,UncommonorCommon.code: "WAITING_FOR_WEBHOOK": payment not confirmed yet, or another open of the same memo is still sending. Not a failure: we call again after about 1 second. It never sends a second card.code: "TURBO_MODE_BUYBACK": a turbo pack rolled Common and was auto-sold;buybackAmountcarries the payout.- Calling it again on an opened memo returns the same award, forever.
- Deadline: open within 2 hours of purchase. After that it returns
400permanently, and a paid, unopened pack is auto-refunded from 2h10m.
Errors: 400 unknown memo or past the deadline, 403 blocked recipient, 404 payment not yet
on chain, 500 machine empty.
GET /api/pack/status?memo=
Everything on record for one memo: purchase, card send, and buybacks. Our main status check.
{ "memo": "…",
"pack": { "wallet": "…", "pack_type": "pokemon_50", "status": "confirmed",
"webhook_received": true, "refunded": null, "created_at": "…" },
"send": { "to_wallet": "…", "nft_address": "…", "status": "confirmed",
"webhook_sent": true, "insured_value": 50, "prize_tier": 4 },
"buyback": [ { "refund_amount": "42500000", "status": "confirmed", "webhook_confirmed": true } ] }See Status lifecycle for how we read these fields.
4. Buyback
GET /api/buyback/available?nft=
{ "available": true, "amount": 42500000, "timeRemaining": 183240 }amount is already adjusted for the pack's buyback percentage. timeRemaining is seconds
left in the window.
POST /api/buyback
{ "playerAddress": "<card owner>", "nftAddress": "<mint>" }
→ { "success": true, "serializedTransaction": "<base64>", "refundAmount": 42500000, "memo": "…" }The owner signs serializedTransaction; it returns the card and pays USDC to the owner.
Eligible for 72 hours after the card was sent. Errors: 400 outside the window or card not
found, 403 blocked, 500 build failed.
GET /api/buyback/check?memo=
{ "exists": true, "nft": "…", "transactionSignature": "…", "buybackAmount": "90000",
"status": "complete" }status is complete once settled, "" while pending.
5. Card data
| Call | Response |
|---|---|
GET /cards/last-updated?page=&step= | [{ nftAddress, insuredValue, updatedAt }], newest first, step ≤ 200 |
GET /metadata/{mint} | Card metadata: name, image, attributes |
We use insuredValue to show card values and to explain buyback prices.
6. Shipping
Collector Crypt Shipping API v1, keyed separately. Full shapes on Shipping.
Not used for new purchases
generatePack (we buy in batches with generateYoloPacks) and generateGift. The gift
claim flow (generatePurchasedPack → usePurchasedPack, where the user signs a message) is
kept only for older gift packs. New purchases are opened straight from the memo, with no user
signature before the reveal, and that is the flow we want from you.