Skip to content
Season structure

Set it up once, and next season inherits it

An organisation, its leagues, its seasons, its divisions, teams, rosters and venues are one structure — not seven spreadsheets that have to agree with each other. Build it once and the fixtures, the scorekeeper console, the standings and the public league page all read from the same rows. When the season closes, roll the structure forward instead of typing it in again.

Every season starts by rebuilding last season

The rosters are in one spreadsheet, the divisions are in another, and the venue list is in an email thread from March. Come registration night, somebody retypes all three — and the retyping is where the errors get in. An over-age player ends up in the wrong division and nobody notices until week four. Two teams share a short code. A coach never got access because the invite went to an address he stopped using two seasons ago. Then the person who kept the master file steps down, and the structure of the league walks out with them.

How it works

Four steps, no configuration

  1. Create the organisation

    A signed-in user creates their own organisation and becomes its league admin. Switch on the sports you actually run — a league can only be created in a sport the organisation has enabled. One click also builds a sample league with teams and a match, if you would rather see the shape of it before typing in your real one.

  2. Lay out the league

    League, then season, then conferences and divisions, teams with their coaches, and venues with their named courts. Rules like period structure and game length are entered once in a common wizard and validated against the chosen sport's own schema, so an invalid setting is rejected at creation rather than at the scorer's table.

  3. Bring the people in

    Rosters typed in, pasted, or uploaded as CSV with a preview that shows per-row errors before anything is written. Invite coaches and players by email, or share a short join code they redeem themselves. Admin invitations are scoped — the whole organisation, one league, or one sport.

  4. Roll it into next season

    A league holds several seasons and exactly one is marked current. When a season finishes, archive the league — teams, rosters, schedule generation and season changes all go read-only — then roll it over into a fresh league that carries the same teams and division structure across, with no match history attached. Bringing the rosters forward is a choice at rollover, not an assumption, so a squad that turns over every year does not inherit last year's names.

Capabilities

What the structure holds

Everything below is built and running today. Unbadged items are included on every plan; a badge means the capability needs a module added to your plan, a higher plan tier, or a piece of setup finished first.

15 of these are included on every plan. One group needs the Tournaments module.

  • Self-serve organization (tenant) creation

    A signed-in user can create their own organization in the app and become its league admin, optionally with a starter league and season created at the same time.

    One self-created organization per user; users already bound to an organization are refused. New organizations are created on the FREE plan tier. Platform/support accounts must use the internal flow instead.

  • Multi-sport organization — 23 sports under one roof

    One organization can run leagues in any of the 23 sports the platform supports, each with its own rules and scoring model, and there is no cap on how many sports an organization enables.

    A sport must be explicitly enabled for the organization before leagues of that sport can be created — league creation returns 403 without a TenantSport row (backend/src/handlers/leagueHandler.ts:564). The Settings → Sports screen only adds sports; disabling is not exposed there and the API has no disable or delete endpoint, so a sport stays enabled once turned on.

  • League creation and multi-league management

    League admins can create and manage any number of leagues inside their organization from a single leagues index, filterable by status and by sport.

    Creating a league requires the LEAGUE_ADMIN role (or platform admin); coaches, players, referees, scorekeepers and fans cannot. League names must be unique within the organization.

  • Season lifecycle (multi-season leagues, activate / end / rollover / archive)

    A league can hold multiple seasons; admins can create, rename, activate and close seasons, archive a finished league to make it read-only, and roll it over into a new league that carries the same teams and division structure with no match history.

    Exactly one season is marked current at a time, enforced in application code rather than by a database constraint. Archived leagues become read-only for teams, rosters, schedule generation and season changes. Roster carry-forward on rollover is opt-in.

  • Conferences and divisions

    Every league can be organised into conferences and divisions with teams assigned to them, edited from a structure tab on the league page, with no paid module required.

    League-admin scope required. Division short names must be unique within a league.

  • Teams and coach assignment

    League admins can create teams within a league, set team branding, assign and remove coaches, and remove a team.

    Team creation is scoped to admins of that league's organization; cross-organization writes are refused. Team short codes must be unique within the league. New teams cannot be added to an archived league.

  • Player profiles and roster management

    Coaches and league admins can add, edit and remove players on a team roster, set jersey numbers, positions and a captain, with positions validated against the league's sport.

    Roster writes require coach-of-team, league-admin, or a club role grant carrying LEAGUE_MANAGE_ROSTER. Rosters lock ahead of upcoming matches. Jersey numbers are unique per team. Archived leagues are read-only.

  • Bulk roster import by paste or CSV

    Admins can paste rows or upload a CSV of players, preview the parsed result with per-row errors before committing, and see a per-row success/failure report with a retry for the failures.

    Parsing and validation happen in the browser and rows are submitted one HTTP request at a time — there is no server-side batch endpoint, so a large import is a sequence of individual creates.

  • Age-based division eligibility enforcement

    Divisions and pools can carry minimum and maximum age bounds, and the platform blocks adding an out-of-age player to a team in that division — both when an admin adds them to a roster and when someone registers.

    Requires a birth date on the player; league admins and above can apply an explicit eligibility override.

  • Venues and courts

    Each league can define venues with structured addresses, capacity, amenities and named courts, and courts can be pre-bound to a division or pool so the scheduler can suggest them.

    Venues belong to a league (not shared org-wide) and are managed by league admins.

  • Sport-specific league rules validated per sport

    League rules such as period structure and game length are entered once in a common wizard, translated into the chosen sport's own vocabulary, and validated against that sport's rule schema so an invalid setting is rejected at creation rather than at scoring time.

    Unknown rule keys are stripped rather than rejected. Leagues created without an explicit sport default to basketball.

  • Scoped administrator invitations (organization, league, or sport)

    An organization owner can invite additional administrators scoped to the whole organization, to one specific league, or to one or more sports, so a sport-scoped admin sees only that sport's leagues.

    Organization-wide co-owner grants are restricted to the organization owner; the bulk grant surface refuses to mint a tenant-wide league admin.

  • Player and coach invitations by email plus league join codes

    Admins can invite people to a league or team by email, revoke or regenerate a pending invite, and share a short league join code that people can redeem to join; invitation links expire after 14 days.

    Invite emails are delivered through a third-party transactional email provider configured per environment; the invite row is still created if the email fails to send. Join codes can be rotated or set to a custom value by an admin.

  • Clubs — grouping leagues, members, staff and custom roles under an organization

    An organization can group its leagues into clubs, each with its own member directory, staff list and custom roles that carry named permissions, and every new league is placed in a club by default.

    Club screens require LEAGUE_ADMIN or platform-admin roles. The organization's Default Club cannot be archived.

  • Per-organization data isolation and role-scoped access

    Every league, season, team and player row is stamped with the organization that owns it, API writes verify the caller administers that specific league, and the API runs with Postgres row-level security enforcement enabled by default.

    Platform support and super-admin accounts intentionally bypass organization scope for support purposes.

  • Tournament pools (group stage)

    Tournaments module

    Group-stage pools can be created inside a league and nested under a parent division, as part of the paid Tournaments module.

    Gated: creating a pool requires the TOURNAMENT_POOLS feature module, which ships defaultEnabled=false and requiresPlan=PRO, so the organization needs the Tournaments entitlement AND a per-league toggle. Listing, editing and deleting existing pools is deliberately left ungated, so pools created before the gate existed stay readable, fixable and removable even if the entitlement is missing or lapses.

Questions

Before you ask

We run several sports. Is that one organisation or several?

One. An organisation can hold leagues in all 23 supported sports, each with its own rules and scoring model, and there is no per-sport charge. The step people miss is the first one: a sport must be switched on for the organisation in sports settings before a league of that sport can be created — until then the API refuses it. Note also that a user self-creates one organisation, not a set of them.

Can we bring our data across from the system we use now?

Partly, and it is worth being blunt about the edges. Rosters can be pasted in or uploaded as CSV with a per-row preview, though a large import is a run of individual creates rather than one batch upload, so give it a moment to finish. Beyond that there is exactly one migration path: a Sports Connect JSON export, dry-run first and designed to be re-run without creating duplicates. It is badged setup-required below rather than included, and it reads export files only — there is no CSV mapper for it and no live connection to Sports Connect's API or anyone else's. Everything else gets typed or pasted.

Can we give someone admin rights over one league only?

Yes. Administrator invitations are scoped to the whole organisation, to a single league, or to one or more sports — a sport-scoped admin sees only that sport's leagues. Organisation-wide co-owner grants stay with the organisation owner and are not issued in bulk. There is a directory listing everyone in the organisation with every grant they hold and what each grant's scope covers; that screen is read-only today, so the granting and revoking itself happens on the surfaces it belongs to.

How do we keep an over-age player out of the wrong division?

Put minimum and maximum ages on the division, and the platform blocks an out-of-age player from being added to a team in it — both when an admin puts them on a roster and when someone registers. It needs a birth date on the player to work, and a league admin can record a deliberate eligibility override when the exception is genuine.

Is a group stage part of this?

Round-robin scheduling is, for a whole league or for a single pool, and it sits in the free core module set. Creating the pools themselves does not: group-stage pools are part of the paid Tournaments module, which needs the Pro entitlement plus a per-league toggle. Knockout-bracket generation is behind that same module. Conferences and divisions, by contrast, are included with no module required.

Set your organisation up once

Public leagues need no account to open.