What it is

A purpose-built giveaway picker on top of /api/pick. POST (or GET) the entrant list — comment usernames, retweet handles, newsletter emails, Discord IDs — and the endpoint returns one winner plus a serverHash that anyone can re-derive against the same list.

The pain point

You ran the giveaway on X. You "drew a winner." The winner happens to be a mutual. Cue the replies. The fix is to never ask anyone to trust the operator — show the math instead. A verifiable giveaway picker gives the runners-up an exact answer to "how do I know you didn't pick your friend?"

Try it live — one winner from a list of usernames

curl "https://api.provable.io/api/pick?clientSeed=giveaway_launch_2026_05_25&items=@alice,@bob,@carol,@dave,@erin,@frank,@grace,@heidi"

Pick multiple winners (1st place, 2nd place, 3rd place) using sequential nonces — the cursor auto-advances:

curl "https://api.provable.io/api/pick?clientSeed=giveaway_launch_2026_05_25_top3&items=@alice,@bob,@carol,@dave,@erin,@frank,@grace,@heidi"

Integration snippet

// 1. Pre-publish the commitment when the giveaway opens.
//    (e.g. pin a tweet with the serverHash before entries are accepted.)
const { serverHash } = await fetch("https://api.provable.io/api/commit", {
  method: "POST",
  headers: { "x-api-key": process.env.PROVABLE_KEY }
}).then((r) => r.json());

// 2. When entries close, snapshot the eligible entries.
const entries = await collectCommentsFromPost(postId);
const usernames = [...new Set(entries.map((c) => c.author))]; // dedupe

// 3. Use a clientSeed nobody controls.
//    The post URL + a future block hash is a clean default.
const clientSeed = `gw_${postId}_${blockHash}`;

const url = new URL("https://api.provable.io/api/pick");
url.searchParams.set("clientSeed", clientSeed);
url.searchParams.set("items", usernames.join(","));

const res = await fetch(url, {
  headers: { "x-api-key": process.env.PROVABLE_KEY }
}).then((r) => r.json());

// 4. Announce: "Winner is @X — verify the draw at /o/{shortId}"
console.log(res.outcome, res.shortId);

Common patterns

Why this is fair

  1. The serverHash is pinned before entries close, so the operator can't pick a seed that targets a specific user.
  2. The clientSeed is chosen from a public unguessable value (block hash, future tweet ID) so entrants can't grind their handles against it either.
  3. The picker math (a uniform integer in [0, N) or a weighted bucket lookup) is implemented in the open-source provable-core library and re-runs identically on every machine.

Where it fits

Related