RoundsWon API
The RoundsWon tournament API: create tournaments, add players, start stages and send match results from your own code, a bot or a game server, with API keys and idempotent requests.
The RoundsWon API lets your own code run tournaments: a Discord bot that adds players, a game server that sends results the moment a match ends, or your own site's admin panel. Anything a host can do on a tournament page, the API can do.
Every change to a tournament is an event, like entry.added or match.result. Your code sends events, and webhooks tell your code about every event that happens, whoever sent it.
For read only widgets on a website you do not need the API at all. See Embed a bracket on any website.
Base address and keys
https://api.freeaffiliate.co/api/tournaments/v1
Make a key on your developer page. Send it on every request:
Authorization: Bearer rw_live_...
A key acts as you, on the tournaments you host or help run as an admin or scorer. Keep it secret like a password. Keys are shown once when made; delete one any time to cut it off.
Endpoints
| Method and path | What it does |
|---|---|
GET /tournaments | The tournaments you host or run as staff, newest first |
POST /tournaments | Creates a tournament (same fields as the create page, with agree: true) |
GET /tournaments/{code} | A tournament's settings, its full state (entries, stages, matches, places, payouts) and seq |
GET /tournaments/{code}/events?after={seq}&limit={n} | The tournament's events in order, from after seq, up to 200 at a time |
POST /tournaments/{code}/events | Sends an event: { "type", "refs", "data" } |
Create a tournament
curl -X POST https://api.freeaffiliate.co/api/tournaments/v1/tournaments \
-H "Authorization: Bearer rw_live_..." \
-H "Content-Type: application/json" \
-d '{"agree":true,"name":"Friday Night Doubles","game":"rocket-league","stages":[{"format":"single","settings":{"bestOf":3}}]}'
agree: true confirms that entry is free and that you pay any prize yourself. The answer has the new tournament's slug, its code.
Send an event
curl -X POST https://api.freeaffiliate.co/api/tournaments/v1/tournaments/abc123/events \
-H "Authorization: Bearer rw_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: match-4821" \
-d '{"type":"match.result","refs":{"stage":0,"match":"r1-0"},"data":{"scores":[2,1]}}'
The answer is the event as stored, plus everything it set off (consequences), like the next match becoming ready or the tournament finishing:
{
"event": { "id": "evt_...", "seq": 14, "type": "match.result", "refs": { "stage": 0, "match": "r1-0" }, "data": { "scores": [2, 1] }, "match": { "status": "done" } },
"consequences": [{ "type": "match.ready", "refs": { "stage": 0, "match": "r2-0" } }]
}
refs say what the event is about: stage (0 for the first), match (its key, see match keys) and entry (an entry's id from the state).
Events your code can send
| Type | refs | data |
|---|---|---|
entry.added | { "name" } a player or team by name | |
entry.updated | entry | { "seed" } |
entry.removed | entry | before the start |
entry.checked_in | entry | |
entry.disqualified | entry | |
stage.started | stage: 0 | starts the tournament, the draw is shuffled fairly |
stage.finished | stage | ends a stage early, like a leaderboard when time is up |
match.result | stage, match | { "scores": [a, b] } head to head, { "sides": [{ "placement", "kills" }] } a lobby, { "score" } a leaderboard score |
score.rejected | stage, match | turns down a leaderboard score |
payout.sent, payout.unsent | entry | marks a prize as paid, or takes that back |
tournament.cancelled | ||
tournament.verified | confirms the final results |
Sending match.result again replaces the result, as long as the matches it feeds have not been played. A player entering with their account and reporting their own score happen on the tournament page, where RoundsWon knows who they are.
Idempotency
Send an Idempotency-Key header with any value you choose, like your own match id. Sending the same request with the same key again returns the event it already made instead of making a second one, so retries after a timeout are always safe.
Errors
Errors answer with a status code and a plain sentence that says what to fix:
{ "error": "In a best of 3 the winner has 2 games." }
| Status | Meaning |
|---|---|
| 400 | Something in the request is wrong, the message says what |
| 401 | The key is missing, wrong or deleted |
| 404 | No such tournament, or your key's account does not run it |
| 409 | The event does not fit right now, like a result for a match nobody is in yet |
| 429 | Too many requests: up to 300 a minute per account |
Staying in step
Every event has a seq, its place in the tournament's list: 1, 2, 3, with no gaps. Keep the last seq you handled. If your code was offline, ask for GET /tournaments/{code}/events?after={seq} and you have everything you missed, in order.