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.
The pain point
Loot boxes and gacha pulls live or die on player trust in drop rates. Regulators in several jurisdictions now require published odds, and players are quick to claim odds change after a major patch ("shadow nerf"). A verifiable loot box API turns drop rates from a marketing claim into a re-runnable calculation.
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
- Published weights, published seed. The drop table is public and the per-pull
serverHashgoes in the player's inventory record. The pull becomes a reproducible calculation, not a marketing claim. - Cannot be shadow-nerfed. Changing the live weights changes the public table — which any data-mining player will notice instantly. The fairness contract is on the published page, not in the server's heart.
- Pity mechanics still work. Pity floors are just a server-side overlay on the raw pull — apply your guarantee after looking at the verifiable outcome, and document the pity rule.
Common patterns
- Item-level rarity. First pick the rarity (weighted), then a uniform pick inside that rarity bucket. Two calls, both verifiable.
- Banner-specific items. Use a banner-prefixed
clientSeed(banner_2026_summer_pull_N) so banner pulls don't share state with the standard pool. - Soft / hard pity. Apply the pity rule server-side after reading the raw outcome. Publish the rule, publish the threshold counter, players can still verify each underlying pull.
- Drop-rate disclosure pages. Pair the loot box with a
/ratesroute that lists current weights and links to the open-source provable-core library.
Where it fits
- Free-to-play mobile games with gacha banners.
- Crate / skin marketplaces for shooters and MOBAs.
- NFT mint reveals with rarity tiers.
- Trading card games opening digital booster packs.