# API reference

Read tournaments, follow them live and import entries and matches from your own systems.

## Introduction

The Roundr API is the same API the Roundr apps use. It speaks JSON over HTTPS, and every path starts with `/v1`.

Integrations authenticate with an API key. The browser apps use the session of the person signed in, and court tablets and displays use the credential they received when they joined.

## API keys

1. An administrator of the organization opens its settings in Control and creates a key.
2. Choose its scopes, reading tournaments, importing participants and matches or both, and when it expires.
3. Copy the key. It starts with `key_` and is shown only once.

> A key can do exactly what its scopes name and nothing else. It never scores a match or changes a draw.

## Authentication

Send the key in the `Authorization` header as a bearer token, or in the `X-API-Key` header.

```
curl https://api.roundr.com/v1/tournaments \
  -H "Authorization: Bearer key_..."
```

## Errors

Every error carries a stable `error_code` next to a readable `message`. Decide on the code; the message is for people and may change.

```
{
  "ok": false,
  "error": { "code": 404, "message": "Not found", "error_code": "not_found" }
}
```

## Rate limits

Every key has a budget for each kind of request, well above what an integration normally sends. Past it the answer is `429` with the error code `rate_limited`, and the `Retry-After` header says when to try again.

## Realtime

To follow a tournament live, read its snapshot once and then listen to its stream of server-sent events. A notification only says that something changed: read the resource again for its state. After an interruption, resume from the last cursor, or read the changes since it.

## Versioning

Changes within `/v1` only ever add: new endpoints, new fields and new error codes. Nothing a client already relies on is removed or changed.

## Endpoints

Every endpoint the API offers, generated from the OpenAPI document the server is tested against. Descriptions are in English. The OpenAPI document, with every schema: https://roundr.com/openapi.json

### Authentication

- `GET /v1/auth/login` Start a browser sign-in
- `GET /v1/auth/callback` Complete a browser sign-in
- `GET /v1/auth/session` Who the caller is, and for which organization
- `POST /v1/auth/organization` Choose the organization a browser session acts for
- `POST /v1/auth/logout` End the browser session
- `GET /v1/auth/options` Which ways in this host offers
- `POST /v1/auth/password/sign-in` Sign in with an email address and a password
- `POST /v1/auth/password/sign-up` Create a Cognito user for an email address
- `POST /v1/auth/password/confirm` Confirm an address with the code Cognito sent it
- `POST /v1/auth/password/resend` Send the confirmation code again
- `POST /v1/auth/password/forgot` Send a password reset code
- `POST /v1/auth/password/reset` Set a new password with the reset code
- `POST /v1/auth/redeem` Turn a one-time court or display code into a credential
- `POST /v1/auth/codes/inspect` Tell what a typed or scanned code is, without spending it
- `POST /v1/auth/setup/join` Make this device a court's Panel with a court setup code
- `GET /v1/tournaments/{tournamentId}/setup-codes` List a tournament's court setup codes
- `POST /v1/tournaments/{tournamentId}/setup-codes` Issue a court setup code
- `DELETE /v1/tournaments/{tournamentId}/setup-codes/{setupCodeId}` Withdraw a court setup code
- `POST /v1/tournaments/{tournamentId}/courts/{courtId}/redemption-codes` Issue a one-time code that admits a scoring device to a court
- `POST /v1/tournaments/{tournamentId}/displays/{displayId}/redemption-codes` Issue a one-time code that admits a venue display
- `POST /v1/organizations` Create an organization

### Account

- `POST /v1/staff-invitations/inspect` Tell what a staff invitation offers, without spending it
- `POST /v1/staff-invitations/redeem` Accept a staff invitation as the signed-in person
- `DELETE /v1/me` Delete the signed-in person's account
- `GET /v1/me/export` A copy of everything Roundr holds about the signed-in person
- `GET /v1/me/matches` The account's own matches
- `GET /v1/me/teams` The league teams the person captains
- `GET /v1/me/teams/{teamId}` A team the person captains, with its roster
- `PUT /v1/me/teams/{teamId}/roster` Replace the roster of a team the person captains
- `GET /v1/me/teams/{teamId}/season` The season of a team the person captains
- `GET /v1/me/teams/{teamId}/fixtures/{fixtureId}/lineup` A line-up of a team the person captains
- `PUT /v1/me/teams/{teamId}/fixtures/{fixtureId}/lineup` Give or change a line-up of a team the person captains
- `GET /v1/me/clubs` The clubs the person is a member of
- `POST /v1/me/clubs` Ask to join a club
- `POST /v1/me/clubs/{membershipId}/accept` Accept a club's invitation
- `POST /v1/me/clubs/{membershipId}/end` Leave a club, or decline or withdraw a proposal
- `GET /v1/me/club-directory` Find a club by name
- `GET /v1/me/players` The players organizations linked to the account
- `DELETE /v1/me/players/{playerId}` Take back an organization's link between the account and a player
- `GET /v1/me/profile` What the account says of the person
- `PUT /v1/me/profile` Give, change or take back the account's date of birth and classes
- `PUT /v1/me/name` Change the name the account is shown under
- `POST /v1/me/legal-acceptances` Accept changed versions of the legal texts
- `GET /v1/me/juniors` The juniors the account is a guardian of
- `POST /v1/me/juniors` Add a junior the account acts for
- `PUT /v1/me/juniors/{juniorId}` Change what a junior's record says
- `DELETE /v1/me/juniors/{juniorId}/guardianship` Stop being a junior's guardian
- `POST /v1/me/juniors/{juniorId}/guardian-invitations` Invite a second guardian by the address of their account
- `DELETE /v1/me/juniors/{juniorId}/guardian-invitations/{invitationId}` Withdraw an invitation to another guardian
- `POST /v1/me/juniors/{juniorId}/handover` Give a junior the code that hands their record over
- `GET /v1/me/guardian-invitations` Invitations to become a junior's guardian
- `DELETE /v1/me/guardian-invitations/{invitationId}` Decline an invitation to become a guardian
- `POST /v1/me/guardian-invitations/{invitationId}/acceptance` Become a junior's guardian
- `POST /v1/me/handover` Take over the record a guardian kept for you
- `GET /v1/me/guardians` The guardians still acting for a person who signs in as themselves
- `DELETE /v1/me/guardians` End every guardianship over the caller
- `GET /v1/me/career` The account's career
- `GET /v1/me/sharing` Whether the account shows its results to players it has played
- `PUT /v1/me/sharing` Show or stop showing the account's results to players it has played
- `GET /v1/me/head-to-head` The players the account can compare itself against
- `GET /v1/me/head-to-head/{playerId}` The account against another it has played
- `GET /v1/me/following` The players the account follows on Live
- `PUT /v1/me/following/{tournamentId}` Replace the players the account follows at a tournament
- `GET /v1/me/notifications` The account's notifications
- `POST /v1/me/notifications/{notificationId}/read` Mark one notification read
- `POST /v1/me/notifications/read-all` Mark every notification read
- `GET /v1/me/notification-preferences` Which notifications come by email and by push
- `PUT /v1/me/notification-preferences` Turn notification emails and pushes on or off
- `DELETE /v1/me/email-suppression` Send email to my address again
- `GET /v1/me/push-subscriptions` The devices the account turned web push on in
- `PUT /v1/me/push-subscriptions` Turn web push on for this browser
- `DELETE /v1/me/push-subscriptions/{subscriptionId}` Turn web push off for a device
- `GET /v1/me/umpired-matches` The matches the account umpired on its own panel
- `GET /v1/me/umpired-matches/{matchId}` One of the account's umpired matches, with its history
- `PUT /v1/me/umpired-matches/{matchId}` Keep a finished match on the account
- `DELETE /v1/me/umpired-matches/{matchId}` Delete one of the account's umpired matches
- `POST /v1/webhooks/ses` SES bounces and complaints, by Amazon SNS

### Tournaments

- `GET /v1/tournaments/{tournamentId}/snapshot` One consistent read of a tournament, and the cursor it was taken at
- `GET /v1/tournaments/{tournamentId}/changes` Durable catch-up on a tournament's notifications
- `GET /v1/tournaments/{tournamentId}/stream` Listen to a tournament's notifications

### Draws

- `GET /v1/tournaments/{tournamentId}/draws` The tournament's draws, drafts and published
- `POST /v1/tournaments/{tournamentId}/draws` Make a draw, as an empty draft
- `GET /v1/draws/{drawId}` One draw, with its entries and its matches
- `DELETE /v1/draws/{drawId}` Delete a draft
- `PUT /v1/draws/{drawId}/entries` List the draft's entries, with their seeds
- `POST /v1/draws/{drawId}/placement` Place every entry in the bracket or into groups
- `POST /v1/draws/{drawId}/seeds-from-ranking` Number the seeds from the entries' ranking
- `PUT /v1/draws/{drawId}/positions` Stand the entries on the lines given
- `POST /v1/draws/{drawId}/publish` Publish the placed draft into its matches
- `PUT /v1/draws/{drawId}/options` Set whether a knockout draft plays for third place, has a consolation and plays off every place
- `PUT /v1/draws/{drawId}/entry-rule` Set who may enter a draft by gender
- `PUT /v1/draws/{drawId}/age-rule` Set a draft's age category
- `PUT /v1/draws/{drawId}/class-rule` Set a draft's class rule
- `PUT /v1/draws/{drawId}/groups` Stand the entries in the groups given
- `GET /v1/draws/{drawId}/standings` A round-robin draw's standings, group by group
- `PUT /v1/draws/{drawId}/feeds` Feed a draft from other draws of its tournament
- `POST /v1/draws/{drawId}/seed-withdrawal` Move the seeds of a published knockout draw after a seed withdraws
- `POST /v1/draws/{drawId}/lots` Draw the lot for a tie in a complete group

### Live

- `GET /v1/public/tournaments` The organization's public page, and its tournaments on Live
- `GET /v1/public/tournaments/{tournamentId}` A tournament published on Live
- `GET /v1/public/matches/{matchId}` One match of a tournament published on Live
- `GET /v1/public/organizations/{slug}` An organization's page on the platform's site
- `GET /v1/public/leagues/{leagueId}` A league published on Live
- `GET /v1/public/pages/tournaments/{tournamentId}` A Live tournament's page, named for a link preview
- `GET /v1/public/pages/matches/{matchId}` A Live match's page, named for a link preview
- `GET /v1/public/pages/leagues/{leagueId}` A Live league's page, named for a link preview

### Registration

- `GET /v1/tournaments/{tournamentId}/registration` Whether the tournament takes entries, until when, and in which events
- `PUT /v1/tournaments/{tournamentId}/registration` Open or close the tournament for registration
- `GET /v1/tournaments/{tournamentId}/registrations` The tournament's registrations
- `POST /v1/tournaments/{tournamentId}/registrations/{registrationId}/decision` Accept a registration into the participants, decline it, or withdraw an accepted one
- `GET /v1/public/tournaments/{tournamentId}/registration` What a tournament offers someone who would enter it
- `GET /v1/me/registrations` The account's registrations and the invitations it was sent
- `POST /v1/me/registrations` Enter an event of a tournament
- `DELETE /v1/me/registrations/{registrationId}` Withdraw an entry, or decline an invitation
- `POST /v1/me/registrations/{registrationId}/confirmation` Confirm a place as the partner a registration named

### Control

- `GET /v1/time-zones` The venue time zones a tournament accepts
- `GET /v1/tournaments` The organization's tournaments
- `POST /v1/tournaments` Create a tournament
- `PATCH /v1/tournaments/{tournamentId}/referee-briefing` Change how every court tablet of a tournament runs its matches
- `PATCH /v1/tournaments/{tournamentId}/live-publication` Publish a tournament on Live, or take it off
- `PATCH /v1/tournaments/{tournamentId}/closure` Close a tournament, or reopen it
- `PATCH /v1/tournaments/{tournamentId}/venue` Name where a tournament is played, or clear it
- `PUT /v1/tournaments/{tournamentId}/details` Correct a tournament's name and dates
- `POST /v1/tournaments/{tournamentId}/copy` Make a new tournament from a tournament's setup
- `PATCH /v1/tournaments/{tournamentId}/schedule-assumptions` Set the match length and rest Control's schedule check works from
- `POST /v1/tournaments/{tournamentId}/courts` Add a court to a tournament
- `PATCH /v1/tournaments/{tournamentId}/courts/{courtId}` Correct a court's name
- `DELETE /v1/tournaments/{tournamentId}/courts/{courtId}` Remove a court nothing uses
- `GET /v1/tournaments/{tournamentId}/displays` List a tournament's hall displays
- `POST /v1/tournaments/{tournamentId}/displays` Add a hall display to a tournament
- `PATCH /v1/tournaments/{tournamentId}/displays/{displayId}` Choose what a hall display shows
- `GET /v1/tournaments/{tournamentId}/staff` List a tournament's directors, referees and umpires
- `POST /v1/tournaments/{tournamentId}/staff` Give a person a tournament role
- `DELETE /v1/tournaments/{tournamentId}/staff/{userId}/{role}` Take a tournament role away
- `GET /v1/tournaments/{tournamentId}/staff-invitations` List a tournament's staff invitations still waiting
- `POST /v1/tournaments/{tournamentId}/staff-invitations` Invite someone to a tournament's staff with a one-time link
- `DELETE /v1/tournaments/{tournamentId}/staff-invitations/{invitationId}` Withdraw a waiting staff invitation
- `GET /v1/organization/administrators` The organization's administrators
- `POST /v1/organization/administrators` Make another person an administrator of the organization
- `DELETE /v1/organization/administrators/{userId}` Take an administrator's role away
- `GET /v1/organization/domains` The organization's hostnames
- `POST /v1/organization/domains/{domainId}/primary` Choose the hostname the organization's links name
- `GET /v1/organization/integration-keys` The organization's API keys
- `POST /v1/organization/integration-keys` Issue an API key
- `DELETE /v1/organization/integration-keys/{keyId}` Revoke an API key
- `GET /v1/organization/plan` The organization's plan
- `POST /v1/organization/billing/checkout-session` Subscribe the organization to a paid plan
- `POST /v1/organization/billing/portal-session` Manage the organization's subscription
- `GET /v1/organization/official-rotations` The organization's rotations of officials
- `POST /v1/organization/official-rotations` Start a rotation of officials
- `GET /v1/organization/official-rotations/{rotationId}` A rotation's board
- `PUT /v1/organization/official-rotations/{rotationId}/courts` Choose the courts a rotation serves
- `POST /v1/organization/official-rotations/{rotationId}/officials` Add an official to a rotation
- `PATCH /v1/organization/official-rotations/{rotationId}/officials/{officialId}` Correct an official
- `PUT /v1/organization/official-rotations/{rotationId}/arrangement` Arrange a rotation's board
- `GET /v1/organization/player-classes` The organization's player classes
- `PUT /v1/organization/player-classes` Name the organization's player classes
- `PUT /v1/organization/name` Rename the organization
- `GET /v1/organization/memberships` The organization's memberships of associations, and of its members
- `POST /v1/organization/memberships` Invite a member, or ask to join an association
- `POST /v1/organization/memberships/{membershipId}/accept` Accept an invitation or a request
- `POST /v1/organization/memberships/{membershipId}/end` End a membership, or decline or withdraw a proposal
- `PUT /v1/organization/memberships/{membershipId}/number` Number a member
- `GET /v1/organization/directory` Find an organization by name
- `GET /v1/organization/club-members` The club's members
- `POST /v1/organization/club-members` Invite a person to the club
- `POST /v1/organization/club-members/{membershipId}/accept` Accept a person's request to join
- `POST /v1/organization/club-members/{membershipId}/end` Remove a member, or decline or withdraw a proposal
- `PUT /v1/organization/club-members/{membershipId}/numbers` The club's numbers for a member
- `GET /v1/organization/clubs` Find a club by name, for the staff
- `GET /v1/organization/leagues` The leagues the organization runs
- `POST /v1/organization/leagues` Make a league
- `GET /v1/organization/leagues/{leagueId}` A league with its divisions and teams
- `PUT /v1/organization/leagues/{leagueId}` Change a league's settings or its tie
- `DELETE /v1/organization/leagues/{leagueId}` Delete a league no team was entered in
- `POST /v1/organization/leagues/{leagueId}/divisions` Add a division below the league's others
- `PUT /v1/organization/leagues/{leagueId}/divisions/{divisionId}` Rename a division
- `DELETE /v1/organization/leagues/{leagueId}/divisions/{divisionId}` Delete a division no team was placed in
- `POST /v1/organization/leagues/{leagueId}/teams` Enter a team by name, for a club that is not on Roundr
- `POST /v1/organization/leagues/{leagueId}/teams/{teamId}/place` Place an entered team in a division, or move a placed one
- `POST /v1/organization/leagues/{leagueId}/teams/{teamId}/remove` Decline an entry, or take a team out of the league
- `GET /v1/organization/leagues/{leagueId}/teams/{teamId}` A team of the league, with its roster and captains
- `GET /v1/organization/leagues/{leagueId}/schedule` A league's fixtures and match days
- `POST /v1/organization/leagues/{leagueId}/divisions/{divisionId}/fixtures` Make a division's fixtures
- `DELETE /v1/organization/leagues/{leagueId}/divisions/{divisionId}/fixtures` Clear a division's fixtures
- `POST /v1/organization/leagues/{leagueId}/match-days` Add a match day
- `DELETE /v1/organization/leagues/{leagueId}/match-days/{matchDayId}` Delete a match day that has not been opened
- `POST /v1/organization/leagues/{leagueId}/match-days/{matchDayId}/open` Open a match day
- `PUT /v1/organization/leagues/{leagueId}/fixtures/{fixtureId}/match-day` Put a fixture on a match day, or take it off one
- `GET /v1/organization/leagues/{leagueId}/standings` Every division's ties and table
- `GET /v1/organization/leagues/{leagueId}/fixtures/{fixtureId}/tie` A tie with both line-ups
- `POST /v1/organization/leagues/{leagueId}/fixtures/{fixtureId}/start` Start a tie
- `GET /v1/organization/teams` The club's teams in leagues
- `POST /v1/organization/teams` Enter a team in a league
- `POST /v1/organization/teams/{teamId}/withdraw` Withdraw the club's team
- `GET /v1/organization/teams/{teamId}` One of the club's teams, with its roster and captains
- `PUT /v1/organization/teams/{teamId}/roster` Replace a team's roster
- `PUT /v1/organization/teams/{teamId}/captains` Name a team's captains
- `GET /v1/organization/teams/{teamId}/season` A club team's season
- `GET /v1/organization/teams/{teamId}/fixtures/{fixtureId}/lineup` A club team's line-up for one of its fixtures
- `PUT /v1/organization/teams/{teamId}/fixtures/{fixtureId}/lineup` Give or change a club team's line-up
- `GET /v1/organization/open-leagues` Leagues the club may enter a team in now
- `GET /v1/tournaments/{tournamentId}/participants` The participants of a tournament, with their players
- `PUT /v1/tournaments/{tournamentId}/players/{playerId}/account` Link a player to a Roundr account
- `DELETE /v1/tournaments/{tournamentId}/players/{playerId}/account` Unlink a player from its Roundr account
- `PUT /v1/tournaments/{tournamentId}/players/{playerId}/classes` Record a player's classes
- `PUT /v1/tournaments/{tournamentId}/players/{playerId}/club` Name the club a player stands for
- `GET /v1/tournaments/{tournamentId}/check-ins` Read who has checked in at the desk
- `PATCH /v1/tournaments/{tournamentId}/players/{playerId}/check-in` Check a player in, take it back, or mark them away
- `POST /v1/tournaments/{tournamentId}/import` Load participants and matches from a draw file
- `POST /v1/tournaments/{tournamentId}/matches` Create one match
- `GET /v1/tournaments/{tournamentId}/results` The tournament's results as CSV
- `GET /v1/tournaments/{tournamentId}/panels` The umpire panels scoring the tournament's matches now
- `PATCH /v1/matches/{matchId}/assignment` Put a match on a court and in a slot, or take it off
- `PATCH /v1/matches/{matchId}/schedule` Move a match in the schedule by the parts named
- `PATCH /v1/tournaments/{tournamentId}/schedule` Move many of the tournament's matches in the schedule at once

### Matches

- `GET /v1/matches/{matchId}` The authoritative state of a match
- `GET /v1/matches/{matchId}/events` The canonical events of a match since a revision
- `POST /v1/matches/{matchId}/commands` Submit one match command
- `POST /v1/matches/{matchId}/scoring-session` Claim a match's scoring session, or take it over
- `DELETE /v1/matches/{matchId}/scoring-session` Release a match's scoring session so the court's device can claim it
- `GET /v1/tournaments/{tournamentId}/courts/{courtId}/open-matches` The matches a court's tablet may take
- `PUT /v1/tournaments/{tournamentId}/courts/{courtId}/matches/{matchId}` Take a waiting match to the court
- `DELETE /v1/tournaments/{tournamentId}/courts/{courtId}/matches/{matchId}` Give a match on the court back before it starts
