What it is

A drop-in randomizer for NFT projects built on /api/shuffle (for token ID assignment and reveal order) and /api/pick (for trait selection from weighted layers). Off-chain, so no LINK token, no gas, no oracle delay — but every assignment is backed by a serverHash committed before mint opens, which a holder can paste into /verify later.

The pain point

Two questions kill NFT trust faster than anything: "did the team mint the legendaries first?" and "was metadata frozen before the reveal?" The on-chain answer is Chainlink VRF, which works but ships gas, an oracle dependency, and a contract upgrade. The off-chain answer is usually a hand-wave. This API gives you a third option — off-chain randomness with the same cryptographic guarantee you'd get from a published VRF proof.

Try it live — shuffle a 100-token reveal order

curl "https://api.provable.io/api/ints?clientSeed=mint_collection_abc_reveal_order&count=10&min=1&max=100"

Or assign a trait per layer for a single token (background, body, eyes, accessory) with published rarity weights:

curl "https://api.provable.io/api/pick?clientSeed=token_42_background&items=sky,sunset,void,rainbow&weights=60,25,10,5"

Integration snippet

// 1. Before mint opens: pin a single commitment for the whole drop.
const { commitId, serverHash } = await fetch(
  "https://api.provable.io/api/commit",
  { method: "POST", headers: { "x-api-key": process.env.PROVABLE_KEY } }
).then((r) => r.json());
publishCollectionMetadata({ serverHash, mintRules: "..." });

// 2. After mint closes: reveal the seed and run the metadata shuffle.
const { serverSeed } = await fetch(
  "https://api.provable.io/api/reveal",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.PROVABLE_KEY,
    },
    body: JSON.stringify({ commitId, clientSeed: `mint_${dropId}` }),
  }
).then((r) => r.json());

// 3. Run the trait shuffle for each token. Each token uses a deterministic
//    sub-seed derived from the revealed seed + tokenId, so the assignment
//    is reproducible by any holder.
async function assignTraits(tokenId, layers) {
  const out = {};
  for (const [layer, options] of Object.entries(layers)) {
    const url = new URL("https://api.provable.io/api/pick");
    url.searchParams.set("clientSeed", `${serverSeed}:${tokenId}:${layer}`);
    url.searchParams.set("items", options.map((o) => o.id).join(","));
    url.searchParams.set("weights", options.map((o) => o.weight).join(","));
    out[layer] = (await fetch(url).then((r) => r.json())).outcome;
  }
  return out;
}

Why this is fair

Common patterns

Where it fits

Related