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 pathWhat it does
GET /tournamentsThe tournaments you host or run as staff, newest first
POST /tournamentsCreates 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}/eventsSends 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

Typerefsdata
entry.added{ "name" } a player or team by name
entry.updatedentry{ "seed" }
entry.removedentrybefore the start
entry.checked_inentry
entry.disqualifiedentry
stage.startedstage: 0starts the tournament, the draw is shuffled fairly
stage.finishedstageends a stage early, like a leaderboard when time is up
match.resultstage, match{ "scores": [a, b] } head to head, { "sides": [{ "placement", "kills" }] } a lobby, { "score" } a leaderboard score
score.rejectedstage, matchturns down a leaderboard score
payout.sent, payout.unsententrymarks a prize as paid, or takes that back
tournament.cancelled
tournament.verifiedconfirms 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." }
StatusMeaning
400Something in the request is wrong, the message says what
401The key is missing, wrong or deleted
404No such tournament, or your key's account does not run it
409The event does not fit right now, like a result for a match nobody is in yet
429Too 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.