What it is
A scientific-grade random sampler built on /api/ints (for index-based selection from a numbered frame) and /api/shuffle (for permutation-based selection without replacement). The seed and the algorithm are public, so any reviewer can replay the draw months or years later and confirm that the published sample matches the published methodology.
A sample 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.
{
"clientSeed": "trial_acme_phase2_sample_2026_05_25",
"nonce": 0,
"serverHash": "949990c4ce02b9308ccf9b41d0a6d15b976d51d26f8d39cec86562c617962d40",
"outcome": [1071, 404, 1218, 975, 2280, 1420, 2802, 1039, 556, 2650]
}- 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) = 949990c4ce02b9308ccf9b41d0a6d15b976d51d26f8d39cec86562c617962d40 - The draw is a pure function of both seeds
HMAC-SHA256(serverSeed, "trial_acme_phase2_sample_2026_05_25:0:0") → 10 integers in 1–5000 = [1071, 404, 1218, 975, 2280, 1420, 2802, 1039, 556, 2650] - After the draw, the seed is revealed and anyone can replay it
serverSeed = 18038732cf04a11ac05c15f119e25c2df7333f018d2e248d06bdf932de2507df
Try it live — draw a sample of 10 from a frame of 5,000
curl "https://api.provable.io/api/ints?clientSeed=trial_acme_phase2_sample_2026_05_25&count=10&min=1&max=5000"
For sampling without replacement, shuffle the numbered frame and take the first N — equivalent to a Fisher-Yates simple random sample:
curl "https://api.provable.io/api/shuffle?clientSeed=audit_invoices_q2_2026_srs&items=INV-001,INV-002,...,INV-015"
Integration snippet
// Stratified random sample: draw n_h units from each stratum independently,
// each with its own seed for separable replay.
async function drawStratifiedSample({ studyId, strata }) {
const out = {};
for (const [stratum, { frame, n }] of Object.entries(strata)) {
const url = new URL("https://api.provable.io/api/shuffle");
url.searchParams.set("clientSeed", `${studyId}:${stratum}`);
url.searchParams.set("items", frame.join(","));
const res = await fetch(url, {
headers: { "x-api-key": process.env.PROVABLE_KEY }
});
const { outcome, serverHash } = await res.json();
out[stratum] = { selected: outcome.slice(0, n), serverHash };
}
return out;
}
const sample = await drawStratifiedSample({
studyId: "trial_acme_phase2",
strata: {
site_us: { frame: usParticipantIds, n: 50 },
site_eu: { frame: euParticipantIds, n: 30 },
site_jp: { frame: jpParticipantIds, n: 20 },
},
});
// Publish: each stratum's serverHash, the published frame, the script.
Why this is reproducible
- Public sampling frame. The frame (list of IDs in the population, or its size N) is published with the protocol. The sampling step is then a pure function of the published seed.
- Cross-language replay. Because the byte stream is HMAC-SHA256 (not language-specific RNG state), any reviewer can re-derive the sample from a Python, R, Go, or browser implementation of the open-source provable-core library.
- Pre-registration friendly. Commit the
serverHashin your pre-registration; reveal theserverSeedin the published paper or audit report. Reviewers can re-derive the sample without re-running your entire pipeline.
Sampling designs that fit
- Simple random sample (SRS). Shuffle the frame, take the first
n. - Stratified random sample. One seeded shuffle per stratum, with a per-stratum
n_h. - Cluster sample. One seeded pick over clusters, then SRS within selected clusters.
- Systematic sample. Draw a single starting integer in
[1, k], then take everyk-th element.
Where it fits
- Clinical trial randomization (allocation lists for treatment vs control).
- Survey research drawing respondents from a frame.
- Financial / compliance audits sampling transactions for inspection.
- Election-day audits selecting precincts or ballots for hand-recount.
- Quality assurance drawing units off a production line.