What it is

A weighted loot drop endpoint built on /api/pick. You hand it the items and their weights — common: 70, rare: 25, epic: 4, legendary: 1 — and it returns one pull plus a serverHash a player can paste into /verify to confirm the published odds were actually applied.

A pull and its proof

This is what anyone can check. The three values in the steps are all a verifier needs, and the replay button recomputes the pull right here, without calling Provable.

GET /api/pick?clientSeed=player_42_crate_2026_05_25&items=common,uncommon,rare,epic,legendary&weights=70,20,7,2.5,0.5Example record
{
  "clientSeed": "player_42_crate_2026_05_25",
  "nonce": 0,
  "serverHash": "8b9046884f4d34e634c7cce0e8d8d7ceb8869ec7bc2779ac37e0398661404afd",
  "outcome": "uncommon",
  "index": 1
}
  1. Before the pull, the server commits to a seedPublish this hash before the pull, so the seed can't be swapped afterwards.serverHash = sha256(serverSeed) = 8b9046884f4d34e634c7cce0e8d8d7ceb8869ec7bc2779ac37e0398661404afd
  2. The pull is a pure function of both seedsHMAC-SHA256(serverSeed, "player_42_crate_2026_05_25:0:0") → u × 100 lands in weight bucket 1 = uncommon
  3. After the pull, the seed is revealed and anyone can replay itserverSeed = 40bff36b5c60686305fbc0f550daeae9c01ce20678c9a08451cea1fce2f82ffd

Try it live — a single pull from a weighted drop table

curl "https://api.provable.io/api/pick?clientSeed=player_42_crate_2026_05_25&items=common,uncommon,rare,epic,legendary&weights=70,20,7,2.5,0.5"

A 10-pull from a banner (issue 10 sequential pulls with a per-pull nonce). The first call is shown; bumping the nonce gives pull #2, #3, etc.:

curl "https://api.provable.io/api/pick?clientSeed=banner_summer_2026_pull_1&items=3star,4star,5star&weights=94.3,5.1,0.6"

Integration snippet

// 1. Publish the loot table somewhere immutable (a blog post,
//    a /rates page on your site, the patch notes).
const TABLE = [
  { id: "common",    weight: 70.0 },
  { id: "uncommon",  weight: 20.0 },
  { id: "rare",      weight: 7.0 },
  { id: "epic",      weight: 2.5 },
  { id: "legendary", weight: 0.5 },
];

async function openCrate(playerId, crateId) {
  const url = new URL("https://api.provable.io/api/pick");
  // One pull per (playerId, crateId) — same seed never re-pulls.
  url.searchParams.set("clientSeed", `crate_${playerId}_${crateId}`);
  url.searchParams.set("items", TABLE.map((t) => t.id).join(","));
  url.searchParams.set("weights", TABLE.map((t) => t.weight).join(","));

  const res = await fetch(url, {
    headers: { "x-api-key": process.env.PROVABLE_KEY }
  });
  const { outcome, serverHash, shortId } = await res.json();
  return { tier: outcome, serverHash, permalink: `/o/${shortId}` };
}

Why this is fair

Common patterns

Where it fits

Related