Skip to Content
Changelog

Changelog

v1.5.0

Playoff brackets: seeded generation, auto-advancement, and consolation placement (2026-07-28)

  • Playoff bracket scheduler — the standalone Playoffs / Bracket division formats now render a drag-and-drop bracket board on the division page (previously only the bracket-size picker). Auto-Populate Games creates the whole bracket from the division’s seed order: first-round games are seeded (1v8, 4v5, 3v6, 2v7 for 8 teams) and later rounds carry “Winner of …” / “Loser of …” placeholders.
  • Automatic advancement — when a bracket game’s score is saved, the winner (and loser, where the bracket routes it) automatically fills the dependent game. Correcting a score re-points the affected games; clearing a score reverts them to placeholders.
  • New format: Playoffs + Consolation (a.k.a. Bracket with Consolation for tournament divisions) — every team ends with a final placement: quarterfinal losers drop to consolation semifinals, whose winners play a 5th Place game and losers a 7th Place game, alongside the 3rd Place game for semifinal losers. Supports 4- and 8-team brackets. New playoffRound values: consolation_semifinal, fifth_place, seventh_place.
  • Public division pages group the new rounds under Playoff Results, and bracket assignments made in the admin scheduler now show up there for all playoff formats. The downloadable scoresheet PDF includes the consolation bracket page.

v1.4.0

Discount codes (2026-07-28)

  • Discount codes — admins create fixed-amount codes per registration (Registrations → Discount Codes: amount off, optional max uses and expiry). Registrants enter a code at the payment step; the discount applies per player on per-player pricing (once per team on per-team), combines with early-bird/late/multi-team pricing, and is locked in when applied. A code covering the full amount completes the registration like a free one — no Stripe checkout. Uses count only on completed payments. New API resource: /api/v1/discountCodes. See Accept Registration Payments — Discount Codes.

v1.3.0

Refunds, AI assistant & scoresheet scanning, AI banners, guided onboarding, registration close gate, team recovery (2026-07-25)

  • Refunds — team-level Refund dialog (Full / specific payment / Manual) plus per-payment partial refunds on the Transactions page. Refunds are recorded as negative payments in the ledger, so collected totals always net out. See Issue Refunds.
  • Payments lifecycle concept page — how a team moves through unpaid → partial → paid → refunded, and how that drives the derived capacity states (secured / waitlisted / pending). See Payments Lifecycle.
  • AI Help Assistant — a floating help button on every admin page opens a chat drawer that answers questions grounded in these docs and your organization’s live data (registrations, capacity, payments). Read-only and scoped to your organization. See AI Help Assistant.
  • AI scoresheet scanning — the new Game Results admin page lists unscored games; photograph a paper scoresheet and AI fills in scores and player stats with per-line confidence for review (basketball: two photos per game; pickleball: one photo per division, plus inline manual entry). See Scan Scoresheets with AI.
  • AI Banner Generation — generate on-brand league banner artwork from Settings → Appearance: pick a vibe and colors, get three variants to choose from. See Generate Banners with AI.
  • Registration end-date gate + time zoneregistration.endDate with config.gateByEndDate closes new sign-ups after a chosen instant (companion to gateByStartDate); one registration.timeZone (IANA) governs how both gates are interpreted and displayed. Existing teams stay editable after close. See Manage Registrations.
  • Admin player editing — each player row on the Team page has an Edit button to correct first name, last name, email, and any custom registration fields. The player’s linked account profile is untouched. If you add or change a player’s email on a waiver-required registration, the waiver signing link is automatically emailed to the new address. See Manage Registrations — Edit a Player.
  • Release statement PDF download — admins can download a clean PDF of a registration’s Release Statement and any required player waivers for a walk-up player to sign. Available from the Team page (“Download Release Form”) and the Registration admin Exports menu. The PDF has no browser headers, page numbers, or URLs. See Manage Registrations — Download a Release Form.
  • Soft-delete team recovery — removing a team now shows an Undo toast, and a collapsed Deleted teams section below the teams table lists removed teams with a one-click Restore (returns the team and its division placement).
  • Payment guard on team removal — a team with unrefunded payments can no longer be removed; refund first. Prevents orphaned money in the ledger.
  • email fieldType — registration form email fields render a dedicated email input, sanitize as you type, and block submission on malformed addresses.
  • Get Started guided onboarding — a 5-step launcher at /admin/get-started takes a new organization from zero to a live (test-mode) registration: build structure (template or describe-it AI), open registration, set a fee, connect Stripe, share the link. See Get Started.
  • Playoff bracket sizeconfig.playoffTeams (2, 4, or 8) on a division selects the generated playoff bracket size.
  • Payments model clarified — Staty is free to use; a 1% platform fee applies to registration payments you collect (Stripe’s processing fee is separate). See Accept Registration Payments.

v1.2.0

Stats visibility, game stat columns, registration start-date gate, and division display controls

  • config.showStats (boolean, config hierarchy) — controls whether stats are visible on public division pages. Supersedes the legacy org-level organization.hideStats flag. Resolve via getShowStats.ts; settable per division/league/tournament in the organize Settings panel. See Divisions — ConfigSchema.
  • config.gameStats (sport-keyed Record<sport, string[]>, config hierarchy) — selects which raw per-game stat keys appear in box scores, game logs, and the admin entry grid. The public aggregate columns (averages) are derived from this whitelist via inferDisplayStats(sport, gameStats). The deprecated flat organization.gameStats field remains only as a legacy fallback.
  • config.hideStandings (boolean, config hierarchy) — hides the public division Standings tab. Independent of hideSchedules.
  • registration.config.gateByStartDate (boolean, registration-level only) — makes a registration publicly viewable while blocking sign-ups until registration.startDate passes. Evaluated client- and server-side via isBeforeRegistrationStart() in registrationSchedule.ts. See Manage Registrations — Registration Start-Date Gate.
  • Email URL encoding fix — several transactional email templates (player_signup, player_payment_required, player_waiver_required, hurry_finish_team_registration, team_*_send_to_admins) had HTML-encoded ampersands (&amp;) in button/link URLs. These are now emitted as literal & so link destinations are correct.
  • API OOM / Redis hardening — the job controller now streams results instead of buffering all at once; the Redis connection is shared across workers to prevent connection-count leaks.

v1.1.0

Opt-in populates via ?include=

  • Extended ?include= to support nested populates and per-field selection. Tokens: a, a.b, a.b.c, a(x,y), a.b(x,y). Multiple tokens with the same root automatically merge. See Query Parameter: ?include=.
  • Breaking: the following GET endpoints previously force-populated reference fields. They now return ObjectId strings unless the caller opts in with ?include=. To preserve old behavior, append the listed include tokens:
    • GET /api/v1/divisions/* — add ?include=league,layout
    • GET /api/v1/games/:id — add ?include=homeTeam.players,awayTeam.players,organization,league
    • GET /api/v1/games/team/:id and /league/:id — add ?include=homeTeam,awayTeam,organization,league (plus ,division for the /league/:id variant)
    • GET /api/v1/leagues/:id, /organization/:id — add ?include=tournament
    • GET /api/v1/leagues/tournament/:id — add ?include=paymentProduct,tournament
    • GET /api/v1/tournaments/:id and /organizationName/:name — add ?include=paymentProduct
    • GET /api/v1/players/team/:id, /division/:id, /registration/:id, /registrationTeam/:id, /game/:id, /league/:id, /leagueName/:name — add ?include=team
    • GET /api/v1/players?team=:id — add &include=team
    • GET /api/v1/players/search — add &include=team,organization
    • GET /api/v1/registrations/:id, /organizationName/:name, /tournament/:id, /league/:id, /organizationName/:name/all — add ?include=contests,paymentProduct,tournament,contest (plus ,league for the /all variant)
    • GET /api/v1/registrationTeams/:id — add ?include=division,playerz
    • GET /api/v1/registrationTeams/registration/:id/all — add ?include=division,requestedDivision,tournament,contest,requestedContest,divisionTeam,user(email,name),registration(paymentProduct)
    • GET /api/v1/registrationTeams/organizationName/:name — add ?include=team,division,requestedDivision.paymentProduct,requestedContest.paymentProduct,tournament.paymentProduct,contest
    • GET /api/v1/teams/:id, /league/:id, /division/:id, /organizationName/:name, /organization/:orgId/division/:divId — add ?include=division
    • GET /api/v1/divisionTeams/:id — add ?include=division,registrationTeam,registration
    • GET /api/v1/divisionTeams/division/:id, /registration/d/:id, /registrationTeam/d/:id — add ?include=division,team (append ,players to also get rosters)
    • GET /api/v1/divisionTeams/league/:id — add ?include=division,team,registrationTeam
    • GET /api/v1/stats/player/:id — add ?include=vsTeam,game,organization(name,_id),league(name,_id),player(name,_id)
    • GET /api/v1/stats/players?id=... — add &include=division(name,_id),organization(name,_id),league(name,_id),team,vsTeam,game,player,player.team,player.organization
    • GET /api/v1/stats/team/:id — add ?include=team,vsTeam,game,player,organization,league
    • GET /api/v1/stats/game/:id — add ?include=player,game
    • GET /api/v1/payments/organization/:name — add ?include=registration,registrationTeam,user(email)
    • GET /api/v1/timeSlots/organizationName/:name, /league/:id, /tournament/:id — add ?include=location.parentLocation
    • GET /api/v1/users/profile, /watchlist, PUT /users/addwatch, PUT /users/removewatch — add ?include=players,players.team,players.organization,players.league
    • GET /api/v1/admins/clerk/:id — add ?include=organizations
  • A handful of write endpoints (notably POST /registrationTeams/:id/playerRemove, /requestTeamAccept, and the admin-dashboard summary endpoints) keep their existing populates because the populated fields drive server-side email and business logic; no caller change needed.

v1.0.0

Initial API release

  • GET/POST/PUT /api/v1/leagues — Season/competition period management
  • GET/POST/PUT /api/v1/tournaments — Bracket tournament management
  • GET/POST/PUT /api/v1/divisions — Division and standings management
  • GET/POST/PUT /api/v1/registrations — Team enrollment windows
  • GET/POST/PUT /api/v1/players — Player profiles and rosters
  • GET/POST/PUT /api/v1/games — Game scheduling and scores (Basketball, Baseball, Judo, Golf, Racket, Track)
  • GET /api/v1/divisionTeams — Division team roster queries with ?include=players
  • Bearer token authentication with scoped API keys
  • Response envelope: { success, data, count }
  • Fair-use rate limiting — there are no per-plan tiers. See Errors & Rate Limits.
Last updated on