A read-only REST API for tournament organizers to fetch their tournament data programmatically. Common use cases include Discord bots that announce results, OBS overlays that render live standings, and stats aggregators that snapshot performance over time. To get started, issue an API key from your tournament's manage page → API keys tab (visit /tournaments to find the tournament you organize).
Base URL: https://dominoleaders.com/api
All authenticated requests require a bearer token in the Authorization header. API keys are scoped to a single tournament and look like dlk_....
curl https://dominoleaders.com/api/v1/tournaments \
-H "Authorization: Bearer dlk_yourkey..."Heads up: the full key is shown only once at issue time. If you lose it, revoke the old key from the manage page and issue a new one.
Public — no authentication required. Useful for liveness probes.
curl https://dominoleaders.com/api/v1/healthResponse:
{ "status": "ok", "time": 1717449600000 }Lists tournaments owned by the organizer who issued the API key.
curl https://dominoleaders.com/api/v1/tournaments \
-H "Authorization: Bearer dlk_yourkey..."Response:
{
"data": [
{
"id": "k973abc1...",
"name": "Spring Championship",
"status": "active",
"startsAt": 1717449600000,
"endsAt": null,
"registrantCount": 32
},
{
"id": "k973abc2...",
"name": "Saturday Showdown",
"status": "completed",
"startsAt": 1714857600000,
"endsAt": 1715116800000,
"registrantCount": 16
}
]
}Returns the full tournament resource: metadata, current standings, and recent matches.
curl https://dominoleaders.com/api/v1/tournaments/k973abc1... \
-H "Authorization: Bearer dlk_yourkey..."Response:
{
"data": {
"id": "k973abc1...",
"name": "Spring Championship",
"description": "Weekly bracket-style tournament for verified players.",
"status": "active",
"startsAt": 1717449600000,
"endsAt": null,
"registrantCount": 32,
"ownerUserId": "us123...",
"rules": "First to 150. No fives. Standard Big-6.",
"standings": [
{
"rank": 1,
"userId": "us999...",
"username": "domino_king",
"displayName": "Domino King",
"profileImageUrl": "https://...",
"wins": 8,
"losses": 1,
"winRate": 0.888,
"eloRating": 1612
}
],
"recentMatches": [
{
"id": "mt001...",
"submitterUserId": "us999...",
"opponentUserId": "us888...",
"winnerUserId": "us999...",
"submitterScore": 150,
"opponentScore": 120,
"submitterEloAfter": 1612,
"opponentEloAfter": 1488,
"screenshotUrls": ["https://..."],
"reviewedAt": 1717445000000
}
]
}
}Just the standings array — a lighter payload optimized for OBS overlays and other high-frequency pollers.
curl https://dominoleaders.com/api/v1/tournaments/k973abc1.../standings \
-H "Authorization: Bearer dlk_yourkey..."Response:
{
"data": [
{
"rank": 1,
"userId": "us999...",
"username": "domino_king",
"displayName": "Domino King",
"profileImageUrl": "https://cdn.../avatar.png",
"wins": 8,
"losses": 1,
"winRate": 0.888,
"eloRating": 1612
},
{
"rank": 2,
"userId": "us888...",
"username": "bone_yard",
"displayName": "Bone Yard",
"profileImageUrl": "https://cdn.../avatar2.png",
"wins": 7,
"losses": 2,
"winRate": 0.777,
"eloRating": 1574
}
]
}Matches for the tournament. Defaults to status=approved; pass pending or rejected to fetch the other queues.
curl "https://dominoleaders.com/api/v1/tournaments/k973abc1.../matches?status=approved" \
-H "Authorization: Bearer dlk_yourkey..."Response:
{
"data": [
{
"id": "mt001...",
"submitterUserId": "us999...",
"opponentUserId": "us888...",
"winnerUserId": "us999...",
"submitterScore": 150,
"opponentScore": 120,
"submitterEloAfter": 1612,
"opponentEloAfter": 1488,
"screenshotUrls": ["https://cdn.../proof.png"],
"reviewedAt": 1717445000000
}
]
}Create a webhook from your tournament's manage page → Webhooks tab. Provide a URL, pick the events you care about, and save. Copy the signing secret immediately — like API keys, it's shown only once.
The following 8 events are supported. Any other event name will be rejected at subscription time.
All deliveries are HTTP POSTs with a JSON body and the following headers:
Content-Type: application/jsonX-DL-Event: <event-name>X-DL-Signature: sha256=<hex> — HMAC SHA-256 of the raw body using your webhook secretX-DL-Delivery: <uuid> — stable per delivery; use as an idempotency keyUser-Agent: DominoLeaders-Webhook/1.0Example body for match.approved:
{
"event": "match.approved",
"deliveredAt": 1717449600000,
"tournamentId": "k973abc1...",
"match": {
"id": "mt001...",
"submitterUserId": "us999...",
"opponentUserId": "us888...",
"winnerUserId": "us999...",
"submitterScore": 150,
"opponentScore": 120,
"submitterEloAfter": 1612,
"opponentEloAfter": 1488,
"screenshotUrls": ["https://cdn.../proof.png"],
"reviewedAt": 1717445000000
}
}Always verify the signature before trusting the payload. Compute an HMAC SHA-256 of the raw request body using your webhook secret and compare to the X-DL-Signature header.
import crypto from "crypto";
import express from "express";
const app = express();
function verify(rawBody: string, signatureHeader: string, secret: string) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return signatureHeader === `sha256=${expected}`;
}
app.post(
"/dl-webhook",
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.header("X-DL-Signature") || "";
if (!verify(req.body.toString("utf8"), sig, process.env.DL_SECRET!)) {
return res.status(401).send("bad signature");
}
const body = JSON.parse(req.body.toString("utf8"));
console.log(body.event, body.tournamentId);
res.sendStatus(200);
}
);Every delivery attempt is logged to the webhookDeliveries table and surfaced on the manage page, including non-2xx responses and timeouts (>10s). For v1 there are no automatic retries — implement idempotency on your side using the X-DL-Delivery header so manual re-deliveries from the dashboard are safe.
There are no formal rate limits today, but please be reasonable — cache standings on your end and avoid sub-second polling. For high-volume integrations (large public leaderboards, multi-stream overlays), reach out at support@dominoleaders.com so we can whitelist you and discuss your needs.
API keys can be revoked at any time from the manage page; revocation is immediate and existing requests in flight will be rejected on their next call.