Independent league-management SaaS product
League Island
A product I own end to end: organizers run registration, competition, and communication in one place instead of a pile of spreadsheets and chat threads.
Organizers can run a branded league from registration through live results, with subscriptions, support, and documentation treated as part of the product.
- Role
- Product owner and builder. I set the roadmap and turned organizer and player needs into product decisions.
- Scale
- Multi-tenant product used to operate leagues, with separate organizer and player workflows.
Customer problem
Running a league is operations work. Someone has to take entries, approve teams, publish a schedule, record a result, tell people where to be, and answer the same question again on the night before a deadline.
Spreadsheets can start that job. They get brittle as soon as a second organizer wants their own brand, or a player expects the schedule to be current without a screenshot in Discord.
Product decisions
The early product assumed one organizer. The decision that mattered was multi-tenancy: each host gets a branded site and an admin console, and players belong to that host.
A second decision was scope. League of Legends workflows (tournament codes, result import, public match context) and a separate live-score game such as pickleball do not belong in one tangle of conditionals. Hosts are scoped by game so permissions and analytics stay understandable.
I also chose boring infrastructure on purpose. The product fails if a captain cannot register, not if the stack is unfashionable.
Roadmap
I sequenced the roadmap as a vertical slice an organizer can finish: create an event, register teams, schedule, run the bracket or round, publish the result. Analytics, overlays, and richer automation came after that path worked.
Payments stayed behind a product gate. Hosts should not turn on paid registration until onboarding can actually charge.
User and administrator workflows
Players need to register, see the schedule, and follow a result. Administrators need approval, seeding, conflict-aware scheduling, and a clear match state. Those are different jobs and different screens.
Real-time updates use server-sent events, with polling as a fallback so a dropped connection does not freeze a public page on a stale score.
Subscription model
Stripe supports paid team registration, including Connect for hosts, checkout, application fees, and webhooks. Money paths are specified in integer cents and reconciled from webhooks, because the browser return is not the source of truth.
I treat subscriptions and paid registration as an operating workflow: retries, refunds, and “can this host charge yet?” are product requirements.
Support and documentation
Support is part of the build. Rules that only live in my head become tickets. Help content sits in the product, split by who is asking — host admin, staff, or player — so the answer is next to the action.
That documentation is also how I keep scope honest. If a rule cannot be explained beside the button, it is probably not ready.
Product analytics
I use product analytics to see whether the lifecycle is actually being used: registration, schedule publication, and completed matches, not just signups. Game-specific views, such as public match charts fed by stored timelines, are there for players and organizers, and they are cached so a popular match does not recompute on every view.
What building it taught me
Owning a product is the commercial loop in miniature: a user need, a decision, a workflow, and a way to know if it worked. Technical range matters because I can ship the workflow. It is not the identity of the work.
The lessons I would repeat are tenancy early, tests on the logic that moves people through a bracket or a fee, and a degradation path for anything live.
Technical footnote
The product is a Next.js application on Supabase (PostgreSQL), with Stripe for payments, the Riot API for League of Legends, and Discord for the communication surface organizers already use. Authentication, row-level access, and webhook idempotency are part of running it, not a separate portfolio piece.