NextRare×Tria

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
} ] }
FieldMeaning
codeThe packType to buy with
enabledMachine toggled on
partnerFeeOur per-pack fee (see Fees)
targetEv / evEV the machine aims for / will deliver right now
oddsPer-tier odds a pack bought now rolls; sum to 1
evLow, evHigh, reachableAchievable EV band on current stock, and whether targetEv is inside it
stock, lowActive cards per tier; a tier below its restock threshold
sellsenabled 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, Uncommon or Common.
  • 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; buybackAmount carries 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 400 permanently, 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

CallResponse
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.

On this page