18+ only. Gambling can be addictive. National Responsible Gambling Programme 0800 006 008.

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.

EndpointWhat it does
GET /roundThe beacon's current and next round with their times, and with ?at=<ISO time> the round a time maps to. Pure arithmetic.
POST /commitBody { 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 /drawBody { 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 /verifyBody { 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.