Skip to content
Reading the season

Answer a stats question without opening a spreadsheet

Every tap a scorekeeper makes becomes a row you can read back: a box score for that match, a season line for that player, a standings row for that team. From there it sorts, filters, splits home from away, compares two to four players side by side, and exports as CSV. The deeper layers — player leaderboards, the season stat grid, and the advanced metrics where basketball goes furthest — are modules a league adds, not things that are simply on.

Three people want three numbers, and all three rebuild the same spreadsheet

The coach wants to know who is leading rebounds before Thursday. The treasurer wants to know what the gate took last month. The board wants participation by club before the next meeting. Today each of those answers is a spreadsheet somebody rebuilds by hand off the scoresheets, and it is stale by the next fixture — so three versions circulate, none of them agree, and the meeting turns into an argument about whose file is right instead of what to do about it. The numbers were captured correctly at the table. They just never came back out in a shape anyone could hand over.

How it works

Four steps, no configuration

  1. Score it once at the table

    Every event a scorekeeper records is stored once. The match box score, a player's season line and the standings row are all built from that same stream, using that sport's own projection — nothing is retyped to produce a report.

  2. Open the grid

    The stat explorer lists every player against every metric their sport publishes. Sort by any column, filter by team, division, games played or player name. It lives in the signed-in section of the app and needs the Leaderboards module switched on for that league.

  3. Narrow it until it answers the question

    Split a table into overall, home or away, scope it to one division or tournament pool, or to a season. Put two to four players — or two to four teams — side by side on their season stat lines.

  4. Take the table with you

    Standings, leaderboards, the stat grid, comparisons, box scores and game logs each have a CSV button. The file is built in the browser from the rows on screen, so filter first and the export matches what you were looking at.

Capabilities

What you can pull out of a season

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.

6 of these are included on every plan. 3 groups need the Leaderboards / Advanced Stats / Ticketing modules.

  • Organization and club reporting with CSV export

    Club administrators can pull cross-league participation, financial and roster reports and download each as a CSV.

    Gated on the CLUB_VIEW_REPORTS capability — available to platform staff, the organization's league admins and owner, or a holder of a custom club role carrying that capability. Read-only.

  • Stat Explorer — sortable, filterable season-stats grid

    Leaderboards module

    A season-stats grid shows every player against every metric their sport publishes, sortable by any column and filterable by team, division, games played and player name.

    The page lives inside the signed-in per-sport section (/s/:slug/explorer) — there is no public fan-page route for it. Metric values and columns are withheld unless the LEADERBOARDS module is on (backend/src/handlers/standingsHandler.ts:180-190). The grid reads at most 500 players in one request (backend/src/services/leaderboardService.ts:384) and sorts, filters and exports in the browser; there is no pagination and no server-side query.

  • Side-by-side compare for players or teams

    Leaderboards module

    Two to four players, or two to four teams, can be compared side by side on their season stat lines, with home/away split selection.

    Hard-limited to between 2 and 4 entities per comparison (backend/src/handlers/standingsHandler.ts:219-220). Signed-in per-sport section only (/s/:slug/compare); no public fan route. Numbers are withheld when the LEADERBOARDS module is off.

  • CSV export from stats and standings screens

    Standings, leaderboards, the stat grid, comparisons, box scores, player game logs and advanced-stats tables each have a CSV export button.

    CSV is the only export format — there is no PDF or Excel export anywhere in this domain. Exports are generated in the browser from the rows currently on screen (frontend/src/lib/csv.ts:13); there is no server-side export endpoint and no scheduled or emailed export.

  • Per-sport box scores for every match

    Every match has a box score built by its sport's own projection of the recorded scoring events, and each of the supported sports ships its own box-score table layout in the app.

    Anonymous access is allowed only when the league is public and ACTIVE or COMPLETED, when the match has streaming enabled, or when the viewer follows the league (backend/src/handlers/matchHandler.ts:277-318). Per-player minutes are derived, not directly recorded: they are reconciled from SUBSTITUTION events plus clock progression, only for sports with a real countdown clock, and a side whose opening lineup cannot be established keeps zero minutes flagged minutesReliable false.

  • Player game log and season stat line on public player pages

    Leaderboards module

    A player page shows that player's season stat line and a per-game log with opponent, result and sport-specific per-game stats, with overall/home/away splits.

    Returns an empty stat line unless the league has the LEADERBOARDS module enabled (backend/src/handlers/playerStatsHandler.ts:317-319). Minutes in the game log are an explicit estimate derived from which periods a player recorded an event in, flagged minutesEstimated on every row until SUBSTITUTION reconciliation lands, and rendered with a leading ≈.

  • Player leaderboards ranked by sport-specific metrics

    Leaderboards module

    Leagues can publish leaderboards that rank players by the metrics their sport defines — points and rebounds for basketball, runs and wickets for cricket, goals and assists for soccer.

    The LEADERBOARDS feature module ships DISABLED by default (infrastructure/lambdas/db-init/platform-features.sql:1355, :1697) — a league admin must turn it on before any player numbers are served; until then the endpoint returns an empty list. A single leaderboard ranks by ONE metric at a time, capped at 100 rows and defaulting to 25; an omitted or unsupported metric falls back to that sport's first published metric.

  • Basketball advanced stats — TENDEX, plus/minus, lineup efficiency, true shooting, usage, off/def rating

    Advanced Stats module

    For basketball, the platform derives advanced metrics — TENDEX, plus/minus, lineup efficiency, true-shooting and effective field-goal percentage, usage and offensive/defensive rating — on demand from the recorded event stream.

    Basketball is the only sport with this layer: supportsAdvancedStats is true only in backend/src/sports/basketball/index.ts:119 and every other plugin sets it false, in which case the endpoints return typed-empty results. The MODULE_ADVANCED_STATS feature module also ships disabled and is seeded as a PRO-plan module (infrastructure/lambdas/db-init/platform-features.sql:2660); with it off the endpoints return an empty shape rather than an error.

  • Sport-native advanced stats for non-basketball sports

    Advanced Stats module

    Most sports declare their own advanced-metric definitions, which the platform derives from recorded events and shows on the match page and in league leaderboards.

    20 of the 23 sport plugins declare the generic advancedStats capability; curling and pool declare none, and basketball uses its separate basketball-only path. Gated behind MODULE_ADVANCED_STATS, which is disabled by default per league.

  • Advanced statistics and analytics module

    Advanced Stats module

    Advanced per-match, per-player and per-lineup statistics, plus sport-specific advanced metrics, are available through the platform's analytics module.

    Gated behind the MODULE_ADVANCED_STATS feature module, which ships disabled by default and is marked as requiring the Pro plan, so the entitlement check runs in addition to the per-league toggle. Basic standings, leaderboards, season stat grids and comparisons sit on the separate free STANDINGS / LEADERBOARDS modules and are unaffected. With the advanced module off these endpoints return an empty payload rather than an error.

  • Pitching statistics (ERA, WHIP, K/9, BB/9, K-BB)

    Leaderboards module

    For baseball and softball, pitching lines are derived from the scored event stream, attributing each outcome to the pitcher on the mound.

    Only baseball and softball declare a pitching capability; every other sport returns an empty shape (backend/src/services/pitchingStatsService.ts:18-20). Gated on the LEADERBOARDS module, which defaults off (backend/src/handlers/pitchingStatsHandler.ts:82).

  • Strength of schedule and recent-form indicators

    Each standings row carries a computed strength-of-schedule rating, and the table shows a recent-form streak for each team.

    Strength of schedule is a derived rating from opponents' records within the same league; it is not an external or cross-league rating system.

  • Home/away splits, division (pool) scoping and season scoping on standings

    Standings can be filtered to overall, home or away records, scoped to a single division or tournament pool, and scoped to a chosen season.

    Pool scoping counts only games where both teams are in that pool. Season scoping requires the league to have Season rows; with no season selected the table is all-time for that league.

  • Ticketing insights (sold, gross, net after fee, sell-through, 30-day sales series)

    Ticketing module

    Organizations get a ticketing insights view with totals for tickets sold, gross sales, net after the platform fee, tickets redeemed, sold-out event count, sell-through percentage, a per-event breakdown and a daily sales series for the last thirty days.

    Scoped to the signed-in user's own organization from their token, entitlement-gated, and the sales series window is fixed at thirty days in code.

  • League-level ticketing summary (printed / sold / redeemed / revenue)

    A league-wide ticketing tab rolls up printed, sold and redeemed ticket counts plus revenue across every ticket type in that league.

    League-admin authorization plus the Ticketing entitlement.

  • Standings and stats tabs on the public fan page

    Leaderboards module

    A league's public fan page can show a standings tab and a stats tab, with the standings columns coming from that league's sport.

    Both tabs are module-gated — standings on STANDINGS, stats on LEADERBOARDS (which defaults off). The league must be marked public and be ACTIVE or COMPLETED, or the viewer must be a signed-in follower via a join code or invite (backend/src/handlers/standingsHandler.ts:34-53); an anonymous reader additionally needs PUBLIC_STATS left on, or both tabs come back empty.

Questions

Before you ask

Can we export to Excel or PDF?

CSV is the only export format in this part of the product. There is no PDF export, no Excel workbook, no scheduled report and nothing emailed to you on a Monday morning. Each export button builds the file in the browser from the rows currently displayed, so a filtered view exports filtered. A CSV opens in Excel, Sheets or Numbers, and that is the intended route.

Which of this is included and which do we pay for?

Standings, per-match box scores, home/away and division splits, strength of schedule and the CSV buttons are included. Player leaderboards, the season stat grid, side-by-side compare, player game logs and pitching lines are part of the Leaderboards module, which ships disabled and is turned on per league. Advanced metrics sit in the Advanced Stats module, which is also off by default and is marked as requiring the Pro plan, so the plan check runs on top of the per-league toggle. Club reports are included; who may open them is controlled by a club-level role capability.

How deep do the stats go for our sport?

It varies, because the depth is set by each sport's own pack rather than one shared template. Basketball goes furthest — the sport-by-sport breakdown is on the Standings & Statistics page. Baseball and softball also get pitching lines: ERA, WHIP, K/9 and BB/9, derived from the scored event stream. Where a sport defines no advanced metrics, those endpoints come back empty rather than inventing a number.

Can fans see any of this, or is it all behind a login?

A league's public fan page can carry a standings tab and a stats tab, with the columns coming from that league's sport. The stats tab is not free: public player stat lines are served by the Leaderboards module, and without it that list comes back empty. The stat explorer and the compare screen are different again — they live in the signed-in section only, and there is no public route to them.

Are the numbers live, or do we wait for someone to run a report?

There is nothing to run. Standings are recomputed when a page asks for them, counting matches that have been marked final — so a game in progress does not move the table, and the table catches up the moment the book closes. Box scores build from the event stream as it lands. Per-player minutes in a game log are derived from which periods a player recorded an event in, and are flagged as an estimate rather than presented as a recorded figure.

Does the money show up in the same place as the stats?

In its own view. Ticketing insights give an organisation tickets sold, gross, net after the platform fee, tickets redeemed, sold-out events, sell-through, a per-event breakdown and a daily sales series for the last thirty days, scoped to your own organisation; a league-level tab rolls up printed, sold and redeemed counts plus revenue. Both need the Ticketing module enabled. Note that online card and PayPal checkout activate only once a payment provider is connected, so today those totals reflect free claims and box-office sales recorded in the app.

Pull the numbers out of your season

Public leagues need no account to open.