Skip to Content
Changelog

Changelog

v1.11.0

Platform fee updated to 1% + $0.30 per registration payment (2026-08-30)

  • The platform fee is now 1% + $0.30 per registration payment (previously 1% flat), deducted automatically from your Stripe payout as before: the price your registrants pay is unchanged, Stripe’s own processing fee (2.9% + $0.30) remains separate, and free registrations and donations still carry no platform fee. When players pay separately, the fee applies to each player’s payment, matching how card processing itself works. Organizations with an existing fee exemption keep it: exempt organizations pay neither the percentage nor the fixed portion. See Accept Registration Payments.

v1.10.0

Test Mode removed: registrations are always live (2026-08-27)

  • Test Mode has been removed. Registrations no longer have a test/live switch: every registration is live, and payments always run through your connected Stripe account. Setting a registration fee now requires a connected Stripe account with charges enabled; free and fee-less registrations work without Stripe. New organizations still start with an active, fee-less registration, so a brand-new org has a working signup link from day one. Historical test payments may still appear in your transaction list; refunding one returns an error explaining that no real charge exists. See Accept Registration Payments.

v1.9.0

One-step competition + registration creation, everything ready at signup (2026-08-25)

  • Creating a tournament or league now creates its registration too: the Competitions tab’s Add Tournament / Add League buttons open a create dialog that sets up the name, contests (tournaments), and sport-template divisions (fully editable), and, unless you uncheck Include registration, opens signup in the same step. Basic Registration applies sensible defaults; Custom Registration drops you straight into the registration’s edit page; an Open registration immediately toggle controls whether signup goes live right away. Signup is free until you attach a fee. See Create a Tournament and Create a League.
  • New organizations start ready to share: creating an organization now also opens a test-mode registration for the sample competition, so a brand-new org has a working signup link from day one (test mode means you can trial the whole flow, payments included, before connecting Stripe).
  • Get Started is now a four-step review checklist: since your competition and registration already exist, the Get Started page walks you through customizing them instead of building from scratch: review & edit (any saved edit checks the step off automatically) or click Looks good to keep the defaults, then set a fee (with Connect Stripe inline in the same step for real payments; test-mode fees need no Stripe) and share your link or embed the form on your own website straight from the last step. The Dashboard setup checklist follows the same review semantics.
  • Safer registration creation: attaching a registration no longer overwrites settings already configured on the tournament or league (they merge now), and a competition can no longer end up with two registrations.

v1.8.0

Platform fee goes live, onboarding fixes, welcome + reminder emails (2026-08-21)

  • The 1% platform fee is now collected on registration payments, exactly as documented on Accept Registration Payments: 1% of each registration payment, deducted automatically from your Stripe payout (the price your registrants pay is unchanged, and Stripe’s own processing fee remains separate). Free registrations, test-mode payments, and donations carry no platform fee, and refunding a payment returns the fee proportionally. Organizations on earlier arrangements keep their existing rate.
  • Structure editing fixed for new accounts: accounts created since mid-July could hit “Access denied” when renaming, configuring, or deleting tournaments, leagues, and divisions from the Structure tab. Organization owners now always have full access to their own organization’s structure.
  • Org creation no longer strands you on the wrong organization: after creating an organization (first or additional), the dashboard now switches to it reliably. If you previously ended up with duplicate organizations while setting up, that was this bug; contact support to clean up the extras.
  • Honest setup checklists: the Get Started progress and the Dashboard setup checklist no longer count the starter structure Staty creates for you as your progress; steps now complete when you actually build your structure, add teams, and score your first game. (The “Setup Stripe Payments” item also previously never showed complete; fixed.)
  • Welcome + reminder emails: new admins receive a short welcome email with the fastest path to going live, and a single “finish setting up” reminder about 48 hours later if setup stalled. Replies go to a real person.
  • New organizations start with a real homepage: instead of a single text welcome page, a fresh org’s public site opens with three starter posts: a hero welcome post with a sport cover photo and Register now button, a “What to expect” overview, and a “Where we play” post with an embedded map centered on the location entered at signup (Los Angeles, CA by default). All three are ordinary posts: edit, re-cover, or delete them in the Site Editor.
  • Playoff tie resolution simplified: when Break playoff ties by standings position is enabled, only standings determine the outcome; manually assigned pre-tournament seeds no longer play any role in tie-breaking. If the two teams can’t be separated by the standings (neither played a pool game), the game stays undecided. The public bracket shows a Standing tag when a tie is awarded this way. See How are tied playoff games decided?.
  • Redesigned banner templates: the template gallery now offers five image-backed designs (Hero, Duotone, Split Panel, Glass, Gradient), each using sport-specific artwork. Banners stored with an older template key are automatically remapped; no changes to existing banners are required.
  • Embed snippet: wallet payment support. The registration embed snippet now includes allow="payment *". If you already have the embed deployed on your site, add this attribute to your <iframe> element to enable Apple Pay and Google Pay inside the frame. Copy the updated snippet from the Embed button on any registration page. See Embed Registration on Your Website.
  • Login rate limiting: repeated failed login attempts are automatically throttled to protect against brute-force attacks.

v1.7.0

Canvas Site Editor, homepage posts feed, division clone, stats/schedule enforcement, and registration fixes (2026-08-17)

  • Canvas Site Editor: the editor is rebuilt as a live canvas: the center of the screen is an exact replica of your public page and you edit directly on it. Content is now block-based (Text, Image, Video, Button, Columns, Divider); a + between blocks inserts a new one, every block has a toolbar (drag, reorder, duplicate, delete), and a right panel holds its settings. Everything saves automatically 1.5 s after your last edit. In the sidebar, Pages lets you create custom navigation pages (name = URL slug, hidden by default, flip Show in navigation to publish), and Division Callouts adds a highlighted notice above any division’s public schedule. Select an image to drag-resize it with a live % badge, or use the Width slider and ⅓/½/⅔/Full quick presets. A Columns block (2–4 columns) supports width ratios like 1:2; columns stack on phones automatically. See Site Editor.

  • Homepage posts feed: your public homepage (/<org>/home) is now a dated announcements feed (game recaps, registration reminders, schedule changes) shown newest-first, with full rich-media support (photos, YouTube/Vimeo/Maps embeds, buttons, columns). New posts start as Drafts; click Publish to go live. Organizations with no published posts show a configurable fallback welcome page (editable under Fallback in the Site Editor). Manage posts from System & Tools → Site Editor. See Admins, Roles & Settings: Post announcements.

  • Right-sidebar widgets: the public org home now shows three live rail widgets alongside the posts feed: Registration CTA (open/opens/closed chip), Upcoming Games, and Latest Results. Widgets self-hide when empty.

  • Division Clone: in the Structure tab, open a division’s ⋯ menu and choose Clone. The copy gets its own name and inherits the division’s settings (tiebreakers, capacity, format, roster bounds, eligibility, stat columns) but starts empty (no teams, no games) and has no payment product; it inherits its price from the level above like any new division. See Divisions, Teams & Rosters: Clone a division.

  • Stats visibility now fully enforced: when Show Stats is off, visitors are blocked from every stats surface: the Leaderboard, Stats, and Game Log tabs disappear and direct URLs (e.g. /team-stats, the player index, /leaderboard) show “Stats aren’t published for this division.” Game pages still show the final score; only per-player box scores are withheld. The setting cascades through the config hierarchy so a contest- or tournament-level toggle covers all its divisions at once. See Games, Scores & Stats: Hide stats.

  • Hide Schedules now removes games from the homepage sidebar: when Hide Schedules is on for a division, contest, or tournament, its games are also removed from the public home’s Upcoming Games, Live Results, and Latest Results rail widgets, so hidden schedules can’t leak onto the front page.

  • Force team onto division: the Add Team dialog on a division page now has a Force team onto division checkbox. When checked (a confirmation appears on toggle), the team is marked accepted and publicly visible immediately, which is useful for teams whose payment and waiver are handled on paper. Without it, teams added to a registration-linked division stay off the public page until accepted normally. See Divisions, Teams & Rosters: Add a team.

  • Bulk import: placement games. Rows whose team cells read 1st Place - <division> vs 2nd Place - <division> (or 3rd/4th) now import as placeholder games. The public standings page shows them under Championship or 3rd Place Game with those labels until the participating teams are decided. See Games, Scores & Stats: Bulk import.

  • Withdrawn teams now counted in Registrations and Players totals: a team that has registered and paid is counted in the Registrations and Players dashboard cards even after it is set to Withdrawn. (Withdrawn teams still hold no capacity spot and are still hidden from /admin/organize and public rosters; only the “how many registrations did we receive” question changed, not capacity.)

  • Registration fee toggle fix: turning a registration’s payment product off (the Payments toggle on the registration page) now reliably removes the fee. Previously the product reference was left in place, so the registration kept charging and the switch sprang back on at the next page load. Existing registrations where you believed the fee was off should be verified.

  • Bold text now renders bold on public sites: text formatted with the toolbar’s bold button now renders at the correct weight on your public league site, matching what you see in the editor. (Previously the bolder CSS keyword resolved to regular weight due to a base font-weight of 100 on the public site.)

  • Teams CSV export: seed column and partial payment status. The Teams CSV download (Registrations → Exports → Teams) now includes a seed column (populated for seeded teams) and distinguishes three payment states: Paid, Partial (paid some but not all), and Unpaid. Stats visibility: one setting (2026-08-17)

  • “Show Stats” is now controlled only by the per-entity setting (Structure → Settings on a tournament, league, or division). The old organization-level “Hide Statistics” toggle was removed; organizations that used it were migrated automatically, so nothing changes on public pages.

v1.6.0

Registration invites, player finish-registration reminders, team page payment editing, embed prefill, scoresheet notes (2026-08-01)

  • Invite a team manager to register: from the Add Team dialog on a division (when a registration exists), tick “Send registration invite to team manager”, enter the manager’s email, and submit. The team appears immediately as a pending registered team; the manager is emailed a link that drops them into the registration with their division preselected, filling in the same team (no duplicate). Resend any time from the Team page (Resend registration invitation). See Manage Registrations: Invite a Team Manager.
  • “Finish your registration” player reminders: on the Team page, when a registration has a player waiver and/or per-player payment, each roster row gets a checkbox. Select players and click Email selected to finish registration; each receives a link to the registration page where they sign in with that email and complete what they still owe (sign waiver and/or pay). Players who are already done are skipped automatically. See Manage Registrations: Remind Players.
  • Team page payment editing: Has Paid, Custom Paid Amount, Paid Notes, and Disable Sync are now editable directly on the Team page, alongside name and coaches. See Accept Registration Payments: Mark a team paid manually.
  • Embed: Player 1 auto-filled from contact info. When a new-team visitor fills out their contact info in the embed registration, the first player slot is pre-filled with their name and email and marked with a “You” badge. The slot is editable and counts toward the roster minimum.
  • Scoresheet PDFs: Notes section on every page. All formats (round-robin, pool play, brackets, consolation) now include a bold “Notes:” label and three ruled write-in lines at the bottom of every page. Game tables reserve this zone so nothing overlaps.
  • Round-robin scheduling: court rotation. The game order within each round now rotates so no team is pinned to the same court every round. Scoresheet PDFs, Auto-Populate, and the public schedule all reflect the new order.
  • Auto-Populate Games / Generate Playoff Bracket no longer require courts: if an organization has no Location/Court records, both buttons are enabled. Generated games have no court until one is assigned.

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, with 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 zone: registration.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 size: config.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