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.

A draw 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 draw right here, without calling Provable.

GET /api/pick?clientSeed=giveaway_launch_2026_05_25&items=@alice,@bob,…,@heidiExample record
{
  "clientSeed": "giveaway_launch_2026_05_25",
  "nonce": 0,
  "serverHash": "7fcc22faaa0d29b92b4a1bda3948d7400ac3f47ba8502013bc6631f42ca4c665",
  "outcome": "@heidi",
  "index": 7
}
  1. Before the draw, the server commits to a seedPublish this hash before the draw, so the seed can't be swapped afterwards.serverHash = sha256(serverSeed) = 7fcc22faaa0d29b92b4a1bda3948d7400ac3f47ba8502013bc6631f42ca4c665
  2. The draw is a pure function of both seedsHMAC-SHA256(serverSeed, "giveaway_launch_2026_05_25:0:0") → index 7 of 8 = @heidi
  3. After the draw, the seed is revealed and anyone can replay itserverSeed = 9f45a4e884e13962116b93e4ffbab002a8a3dd4c5c7625a3c36edf900ab83ace

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. When entries close, snapshot and dedupe the eligible entries.
const entries = await collectCommentsFromPost(postId);
const usernames = [...new Set(entries.map((c) => c.author))];

// 2. Commit, and pin a post with the serverHash and the username list.
const commit = await fetch("https://api.provable.io/api/commit", {
  method: "POST",
  headers: { "x-api-key": process.env.PROVABLE_KEY }
}).then((r) => r.json());

// 3. Reveal within 10 minutes with a clientSeed nobody knew at commit time.
//    The post URL + the next block hash is a clean default.
const draw = await fetch("https://api.provable.io/api/reveal", {
  method: "POST",
  headers: { "x-api-key": process.env.PROVABLE_KEY, "content-type": "application/json" },
  body: JSON.stringify({
    commitId: commit.commitId,
    clientSeed: `gw_${postId}_${blockHash}`,
    endpoint: "ints",
    params: { count: 1, min: 1, max: usernames.length }
  })
}).then((r) => r.json());

// 4. Announce: "Winner is @X", with the serverHash, serverSeed and clientSeed.
console.log(usernames[draw.outcome[0] - 1], draw.serverSeed);

Common patterns

Why this is fair

  1. The serverHash is pinned before the client seed exists, and the username list is frozen before the commit, 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