Developers
Fair Draw API and library
Everything the tool does, from code. Four endpoints with no key and no account, and a JavaScript library that runs the whole thing offline, signature check included. Both are free and open source.
curl -X POST https://bettip.co.za/api/v1/fair-draw/commit -H 'content-type: application/json' \
-d '{"entries":["Thabo","Aisha","Pieter","Naledi"],"pick":1}' Endpoints
Base URL https://bettip.co.za/api/v1/fair-draw. JSON in, JSON out. Nothing is stored: the same draw description always gives the same winners.
| Endpoint | What it does |
|---|---|
GET /round | The beacon's current and next round with their times, and with ?at=<ISO time> the round a time maps to. Pure arithmetic. |
POST /commit | Body { entries | text | range, pick, title?, at? | round? }. Returns the draw description bound to a round, its commitment and two links: the commitment link to post, and the draw link. With no time, the round is about twelve seconds ahead. |
POST /draw | Body { spec } or { draw: "<draw link>" }. Reads the round from two relays, verifies the signature, derives the winners, returns the proof and the result link. 425 with secondsToGo while the round has not landed. |
POST /verify | Body { proof } or { link: "<result link>" }. Re-runs every check and reports each one, then confirms the relays still publish the same output. |
OpenAPI description: /fair-draw/openapi.json. Limits: 50,000 entries, 200 characters each, 10,000 winners, request bodies up to 1.5 MB.
A whole draw in three calls
# 1. describe the draw; the round is chosen for you, about 12 s ahead
curl -s -X POST https://bettip.co.za/api/v1/fair-draw/commit -H 'content-type: application/json' \
-d '{"text":"Thabo\nAisha\nPieter\nNaledi","pick":2,"title":"Friday giveaway"}'
# -> data.spec, data.commitment, data.landsAt, data.links.commitment, data.links.draw
# 2. once landsAt has passed, draw (repeat on 425 until the round lands)
curl -s -X POST https://bettip.co.za/api/v1/fair-draw/draw -H 'content-type: application/json' \
-d '{"spec": <data.spec from step 1>}'
# -> data.winners, data.proof, data.links.result
# 3. anyone can verify, from the link alone
curl -s -X POST https://bettip.co.za/api/v1/fair-draw/verify -H 'content-type: application/json' \
-d '{"link":"<data.links.result>"}'
# -> ok, data.checks (each named and explained), data.winners The library
The API is a thin wrapper over @bettip/fair-draw, which is what the pages here run in the browser. It has two dependencies, both audited: @noble/curves for BLS12-381 and @noble/hashes for SHA-256. It works in Node 20+, browsers and Cloudflare Workers.
npm install @bettip/fair-draw import { createSpec, fetchBeacon, makeProof, verifyProof, links } from '@bettip/fair-draw';
const spec = createSpec({ entries: ['Thabo', 'Aisha', 'Pieter', 'Naledi'], pick: 2, at: Date.now() + 60_000 });
const { commitment } = await links(spec); // post this first, if entrants must see the list was fixed
// later, once spec.round has landed:
const beacon = await fetchBeacon(spec.round); // two relays must agree; the BLS signature is verified
const proof = makeProof(spec, beacon);
console.log(proof.winners, verifyProof(proof).ok); // [{ position: 1, index: 2, entry: 'Pieter' }, ...] true Source, tests and the specification with test vectors: github.com/bettip-co-za/fair-draw. MIT licence. If you host your own copy of the tool, a link back here is appreciated and not required.
Why bother with an API
A Discord bot that picks a winner, a raffle page that shows its proof, a classroom tool that picks teams: each gets the same guarantee as the page here, and each result link opens on this site with the checks re-run. The randomness is drand's, the rule is public, and the proof is portable.
Terms, in plain words
Free for any use. No key, no quota worth stating; if a client hammers the endpoints it may be slowed. Nothing sent to /commit, /draw or /verify is kept. The beacon belongs to the League of Entropy; if it is unreachable the API says so rather than inventing a number.