Authentication
Create a key in Settings → Developer. The full token is shown once and starts with brk_live_. Send it on every /v1 request — either header works:
X-Api-Key: brk_live_…
Authorization: Bearer brk_live_…- read — list and fetch tournaments, participants and matches.
- write — add participants, report scores, start or finalize. GET routes still work on a write-only key.
- The key acts as its owner. Public tournaments are readable; private ones only if you can manage them.
Interactive try-it-out lives on Swagger.
Endpoints
Base URL https://bracket.arrobin.com/backend. JSON in and out. :idOrSlug accepts a tournament cuid or its public slug.
/v1/mereadConfirm the key works. Returns the owner’s user id and the key’s scopes.
curl https://bracket.arrobin.com/backend/v1/me \
-H "X-Api-Key: brk_live_…"/v1/tournamentsreadTournaments you created or administer (not the public discovery catalog).
curl https://bracket.arrobin.com/backend/v1/tournaments \
-H "X-Api-Key: brk_live_…"/v1/tournaments/:idOrSlugreadOne tournament by cuid or slug. Private events 404 unless the key owner can manage them.
Query include=participants,matches,standings — comma-separated extras.
curl "https://bracket.arrobin.com/backend/v1/tournaments/summer-open?include=participants,matches" \
-H "X-Api-Key: brk_live_…"/v1/tournaments/:idOrSlug/participantsreadTeams / players in seed order, including roster rows.
curl https://bracket.arrobin.com/backend/v1/tournaments/summer-open/participants \
-H "X-Api-Key: brk_live_…"/v1/tournaments/:idOrSlug/participantswriteAdd a participant before the bracket is generated.
Body { "name": "Team Alpha", "seed": 1, "players": ["Ada"] }
Fails after generate (bracket_generated), on duplicate names, or when the event is full.
curl https://bracket.arrobin.com/backend/v1/tournaments/summer-open/participants \
-H "X-Api-Key: brk_live_…" \
-H "Content-Type: application/json" \
-d '{"name":"Team Alpha","seed":1}'/v1/tournaments/:idOrSlug/matchesreadNon-bye matches. Filter with state=open (ready to play), complete, or all (default).
Query state=open | complete | all
curl "https://bracket.arrobin.com/backend/v1/tournaments/summer-open/matches?state=open" \
-H "X-Api-Key: brk_live_…"/v1/matches/:idwriteReport a result. You must manage the parent tournament.
Body { "homeScore": 2, "awayScore": 1, "winnerId": "optional_team_id", "sets": [{"home":11,"away":7}] }
If sets[] is sent, scores become set-wins. winnerId must be home or away. Omit it to infer from scores (equal scores are a draw).
curl https://bracket.arrobin.com/backend/v1/matches/MATCH_ID -X PUT \
-H "X-Api-Key: brk_live_…" \
-H "Content-Type: application/json" \
-d '{"homeScore":2,"awayScore":1}'/v1/tournaments/:idOrSlug/startwriteGenerate the bracket from saved settings. Owner only — admins get owner_only.
curl https://bracket.arrobin.com/backend/v1/tournaments/summer-open/start -X POST \
-H "X-Api-Key: brk_live_…"/v1/tournaments/:idOrSlug/finalizewriteMark the tournament completed and fire tournament.completed. Idempotent if already done.
curl https://bracket.arrobin.com/backend/v1/tournaments/summer-open/finalize -X POST \
-H "X-Api-Key: brk_live_…"Errors
Failed /v1 responses use a single envelope. Validation failures may add details.
{
"error": {
"code": "not_found",
"message": "Tournament not found"
}
}| Code | When |
|---|---|
| unauthorized | Missing API key |
| invalid_api_key | Unknown or revoked key |
| insufficient_scope | Write endpoint called with a read-only key |
| forbidden / owner_only | You do not manage this tournament (start is owner-only) |
| not_found | Unknown id — or a private event you cannot see |
| rate_limited | 120 requests per minute per key |
| validation_error | Body failed schema checks |
Webhooks
Register an HTTPS URL in Developer settings (or a tournament’s Integrations tab). We POST JSON within 5 seconds and sign the raw body. After 20 consecutive failures the hook is paused. Test deliveries add X-Bracket-Test: 1.
Headers
X-Bracket-Event— event name, e.g.match.completedX-Bracket-Signature—sha256=plus hex HMAC-SHA256 of the raw bodyX-Bracket-Delivery— unique delivery idUser-Agent—Bracket-Webhooks/1.0
Events
tournament.startedTournament started (bracket generated)
tournament.completedTournament completed (final results)
match.readyMatch ready to play
match.completedMatch result reported
participant.registeredParticipant registered
registration.approvedRegistration approved
schedule.updatedSchedule updated
announcement.postedAnnouncement posted
Payload
Every delivery is { id, event, createdAt, tournamentId, data }. When we know the tournament we attach a short summary on data.tournament; match events also include data.match. A test ping sends { test: true, message: "Test delivery from Bracket" } as data.
{
"id": "8f3c1e2a-…",
"event": "match.completed",
"createdAt": "2026-09-05T12:00:00.000Z",
"tournamentId": "clxxxxxxxxxxxxxxxxxxxxxxxx",
"data": {
"tournament": { "id": "…", "slug": "summer-open", "name": "Summer Open", "status": "ACTIVE", "format": "SINGLE_ELIM" },
"match": { "id": "…", "round": 2, "status": "COMPLETED", "homeScore": 2, "awayScore": 1 }
}
}Verify the signature (Node)
Hash the exact bytes you received. Re-serializing JSON will fail the check.
const crypto = require('crypto');
function verifySignature(rawBody, secret, header) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const a = Buffer.from(header ?? '', 'utf8');
const b = Buffer.from(expected, 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express: app.use(express.raw({ type: 'application/json' }))
// then verifySignature(req.body, secret, req.get('X-Bracket-Signature'))Rate limits
120 requests per minute per API key. Responses include X-RateLimit-Limit and X-RateLimit-Remaining. Over the limit you get 429 with Retry-After.
Ready to call it?
Mint a key, then try the live schema explorer.