HKPL · New Season Setup

Start a new season — from an empty league to opening day.

The exact order to stand up a fresh season: create the season, build the divisions and their rulebook, register clubs and teams, fill the rosters, generate the schedule, and open. Each step says who does it, where, what to type, and how to check it worked. Practise it on the sandbox; it's the same on the live league.

Before you start — gather these

Setup goes fast if you have the decisions made first. Bring:

📋 Your season sheet

  • Season name & dates — e.g. "Season 3 2026", start & end.
  • The divisions and, for each, its DUPR rating band (e.g. A = open, B = 3.000–3.699, C = ≤ 2.999), whether unrated (NR) players are allowed, and the rating cut-off date.
  • Roster rules — max players per team, minimum by gender, minimum age.
  • Fees — club registration fee, team registration fee (so Finance can check receipts).
  • The clubs joining, and who manages each.
  • Schedule capacity — how many match-days, time-slots per day, and courts (this sets how many fixtures fit).
You don't start from a blank rulebook. A league begins with a copy of the standard pickleball rulebook already loaded — you tune the numbers for your season, you don't write rules from scratch.

The setup sequence

Do these in order — each step feeds the next. Everything below is done as the League Admin in /admin.html unless noted.

The screenshots below are real. A test robot actually walked this flow on the sandbox — it created the division, opened the forms, added a player — and captured each screen. The arrows mark exactly what to click or look at. If the robot can drive it, the feature genuinely works.
1

Create the season

League Admin · Admin → Seasons
  1. Open Admin → Seasons → + New season.
  2. Enter the name and year (e.g. "Season 3 2026") and its start/end dates.
  3. Mark it the current season — this is the container everything else hangs off.
Verify The new season shows in the Seasons list as "current".
2

Create the divisions — and set each one's rating band

League Admin · Admin → Divisions

For every division in the season:

  1. Admin → Divisions → + New division. Name it (Division A, B, C…) and attach it to the season.
  2. Set its DUPR band: doubles min rating and doubles max rating (and singles if you run singles). Leave a bound blank for "no limit" (e.g. Division A open = both blank).
  3. Set the rating cut-off date — the day a player's rating is judged against the band.
Why this is the rulebook The band you type here is the number the system enforces. In Step 3 you switch the check on; the numbers come from this form. One source of truth — change the band here and enforcement follows.
Verify The Divisions table shows each division with its band, e.g. 3.000–3.699 · cutoff 01 Jan.
New Division form with DUPR band filled
Real screenshot. Creating a division — we set the band 3.000–3.500. The arrow marks the field that becomes the enforced rule.
The created division in the Divisions list
It's really there. The division we just created now sits in the live Divisions list, band shown.
3

Switch on the rulebook — decide what blocks and what only warns

League Admin · Admin → Rule Builder · Admin → Rulebook

The band from Step 2 only bites once a rule turns it on. In Rule Builder, for each division set the rules and their strength — block (refuse) or warn (allow but flag):

RuleWhat it doesTypical
Player in DUPR bandRefuses a player whose rating is outside the division band from Step 2block
Roster capMax players per teamblock (e.g. 20)
One team per seasonA player can't be on two teamsblock
Unrated (NR) allowed?Whether players with no DUPR may joinper division
Minimum by gendere.g. at least 2 male on a rosterblock
Minimum ageAge floorwarn
Registration deadlineNo new players after a dateblock

Write the human-readable rulebook text in Admin → Rulebook so members can read the rules; the enforceable versions live in Rule Builder.

This is what makes registration guard. With these on, an ineligible player is refused at Add-Player time with the reason — not caught later by a human. You can see it live on the sandbox right now: the rulebook already refuses adds that break one-team-per-season, the player-registration deadline and the age check. Set a division's DUPR band (Step 2) and switch on "Player in DUPR band" here, and an out-of-band add is refused the same way — same mechanism, one more rule.
Verify Become a captain of that division and try to add an out-of-band player — you should be refused with the rule's message.
4

Register the clubs

Clubs self-register → Registrar approves · or Admin adds directly · /register.html · Admin → Clubs
  1. Self-serve: a club fills /register.html (name, district, contact) and uploads its registration-fee receipt. It lands in the Registrar queue.
  2. The Registrar (and Finance) open the pending club, check the receipt, and Approve — now the club is live.
  3. Assign a Club Manager in Admin → Club Managers so someone can run that club's page and register its teams.
Verify Approved clubs appear on /clubs.html; the club manager can now open the club's dashboard.
Club Manager dashboard
Real screenshot — the Club Manager dashboard on the club page: edit banner, logo, colours (live preview), description and sponsors.
5

Register the teams into divisions

Club Manager registers → Registrar approves · /club.html → Register a new team
  1. The Club Manager opens the club page → "Register a new team", names it, picks the division, sets day-of-play / home court, and uploads the team fee receipt (required).
  2. It goes to the Registrar queue → Approve → the team is placed in that division.
Placing the team fixes its rulebook. Whichever division you register into decides which rating band and rules its roster must obey.
Verify The team shows under the club and in Admin → Teams in the right division.
Register a new team form
Real screenshot — registering a team into a division from the club page. A payment receipt is required.
6

Build the rosters — add players

Captain · /app.html → Add Player
  1. The Captain opens the team in /app.htmlAdd Player → search by DUPR ID / name / email.
  2. The rulebook checks every add — band, roster cap, one-team-per-season, already-on-team. An ineligible player is refused with the reason; an eligible one becomes a pending invitation.
  3. The player logs into the app and taps Accept (on the live league they also get an email/SMS invite; on the sandbox it's in-app only).
  4. Repeat for co-captains and the rest of the roster.
Verify The roster fills; each player shows "pending invite" → "active" after they accept. Try adding a player who's already on a team this season — it's refused with the reason. (Once you've set a division band + switched on the band rule, an out-of-band add is refused too.)
Captain Add Player dialog
Real screenshot — the captain's Add Player dialog. Every add runs through the division rulebook.
Rulebook refusal
Real refusal. The captain's Add-Player review runs every add through the rulebook. Here it refused the add — "already has this player this season — one team per season" — and flags the age check and past registration deadline. Submit is disabled. Live app response, not a mock.
7

Generate the schedule

League Admin · Admin → ⚡ Generate Schedule · /schedule-generator.html
  1. Open ⚡ Generate Schedule. Set the capacity: match-days × time-slots per day × courts — this is how many fixtures fit.
  2. Generate the round-robin fixtures for each division.
  3. If it says "N matches couldn't be placed", increase match-days / slots / courts and regenerate.
Verify Fixtures appear on /schedule.html and on each captain's /app.html.
8

Pre-flight, then open the season

League Admin · Admin → health card

Before you announce it, run the two one-click checks on the Admin health card:

Integrity check → 0 errors, 0 warnings. (No orphan rosters, no half-built matches, no duplicate DUPR ids.)
DUPR health board → Full run → all functions green. (Ratings can be read and results can be posted.)
Every division has a band + rules on; every team is in a division; rosters look right.
Schedule generated with no unplaced matches.

Then post the opening announcement (Admin → Notifications) and you're live.

Admin DUPR health board
Real screenshot. The admin health card — "Run full functional test" calls DUPR live; statuses are real, "nothing is faked green".
Admin integrity check
Real screenshot. The integrity check cross-checks the whole database — last run 0 errors, 0 warnings.

Doing this on the real league (production)

The steps are identical. The only differences on hkpl.silkvo.com vs the sandbox:

On the sandbox nowOn the live league
Player invites are in-app only (email caged)Real invitation emails/SMS go out
DUPR results marked "submitted (sandbox)"Written to the real DUPR Partner API — ratings move
One-click role login for testingEveryone signs in with their own account
Dummy clubs/teams/playersYour real season data
Go-live switch: when the real season is set up this way, the engineer flips three switches — real DUPR partner credentials, real email/SMS, real sign-in — and re-runs the two pre-flight checks on production. Then open the doors.