Items
What is being dropped: taxonomy, price, images, variants and stock.
Everything on this page is what the hosted storefront and admin portal are built on. If they can do it, so can you — from your own code, in any language that speaks HTTPS.
curl https://api.yourhost.example/api/v1/Lottery/list \
-H "X-Api-Key: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{}'{
"total": 3,
"items": [
{
"id": "velocity-4-midnight",
"status": "Open",
"closesOn": "2026-10-05T14:00:00Z",
"winnerCount": 25,
"backupCount": 10
}
]
}Composable filters in the body, paging in the query — the same shape on every resource.
People sign in; integrations hold a key. Both are checked against live permissions on every request, so a revoked grant stops working immediately — nothing is baked into a token.
A key is issued by a signed-in user and can hold only permissions that user holds in full — delegation, never escalation. Holding one action in an area never mints a key for the whole area.
Our TypeScript client is generated from the OpenAPI document at /api/swagger — the same client the hosted apps run on — so nothing they can do is missing from it.
At a glance
Every resource follows the same shape — get, list with composable filters, count, create, update, delete, plus bulk variants — so the second one you learn is free.
What is being dropped: taxonomy, price, images, variants and stock.
The drop itself: window, winner and backup counts, pools, entry rules, allocations.
One entry per user per drop with a quantity; wins carry the claim window and rank.
Created on claim with the winner’s fulfilment choice; fulfilled or cancelled by you.
The in-app inbox, per-channel preferences and “notify me” follows on any entity.
Endpoints, subscribed events, secret rotation, test pings and delivery history.
Deletes are soft. Every write is audited with who, what, when and from where, and each entity keeps a human-readable event timeline.
Six states, one direction. A scheduler moves drops along on time; every transition is a conditional write, so a draw can never run twice and a crashed draw resumes with the same winners.
Being configured. Never visible publicly.
Accepting entries until the published close.
Entries locked, draw seed committed as a hash.
The draw runs — a gate no second draw can pass.
Winners claiming, standbys promoted on a pass.
Every unit claimed or returned to stock.
Once a draw has run there are winners with claim windows; after that point a drop can be completed, never undone.
A drop opens only if stock covers it after what other live drops on the item already commit.
A claim creates an order and decrements stock; cancelling restores it and releases the win to the next standby.
A storefront has visitors before it has users, so browsing needs no token. These routes are anonymous and rate-limited, keyed by your host.
Entering, claiming and preferences are authenticated, with ownership enforced server-side. Draft and cancelled drops are never visible here, and the draw seed never leaves the API before the draw has run.
Anonymous · GET
Register an https endpoint and subscribe to flat event names — any resource’s create, update or delete, the curated lifecycle events, or wildcards such as lottery.*. A test ping is one call away.
Exponential backoff; retries and redeliveries are byte-identical to the original payload.
Reveal on demand; rotation is a version bump with a hard cutover.
Keep failing and the endpoint is disabled automatically; re-enable with one call.
An endpoint receives its own tenant’s events and never another’s.
DropLoop-Signature: t=1757095214,v1=<hex> DropLoop-Webhook-Id: <deliveryId> DropLoop-Webhook-Event: lottery.drawn DropLoop-Webhook-Attempt: 1
const [t, v1] = signature
.split(',')
.map((part) => part.slice(part.indexOf('=') + 1))
const expected = hmacSha256Hex(secret, `${t}.${rawBody}`)
if (!timingSafeEqual(expected, v1)) reject()
if (Math.abs(nowSeconds() - Number(t)) > 300) reject()Compute over the raw request body, compare in constant time, and reject stale timestamps. The secret is the whole whsec_… string.
Commit, draw, reveal. Anyone can check that the seed was fixed before the entrant list was known and that the published winners follow from it — without trusting you or us.
When the window closes, a random seed is generated and only its SHA-256 hash is stored with the drop. The seed itself never leaves the API before the draw.
Entrants are sorted and hashed, then winners and standbys are picked from a random source seeded with that seed. The snapshot and every pick are audited.
Results reveal the seed, the entrant hash and the algorithm. The public verify endpoint replays the draw and reports whether the published winners follow.
The parts that keep an integration honest under load.
The in-app inbox is pushed live over a SignalR hub at /hubs/notifications, authenticated with the same bearer token. Email, SMS and push fan out per notification type according to your host’s channel map, and every user controls their own mutes per type and channel.
402 plan_limit_exceeded412 precondition failed409 conflictGet a host provisioned, an API key issued and a webhook pointed at your stack — then run a real draw end to end.