Releases: jchette/CourtPin
v2.0.0 - Event access support
What's new in v2.0.0
🎉 Major feature — CourtReserve event access
Members registered for events now receive door access automatically, just like court reservations. Three access modes are available via the new EVENT_ACCESS_MODE environment variable.
Access modes
pin_individual (default)
Each registrant receives their own unique UniFi visitor record and PIN.
Works identically to court reservations.
pin_shared
One UniFi visitor and PIN is created for the event. All registrants receive the same PIN. New registrants who sign up after the initial notification always receive the PIN on the next cycle.
unlock
Door(s) in UNIFI_RESOURCES are unlocked for the full event window and automatically relock when the event ends. No PINs generated.
Set EVENT_UNLOCK_NOTIFY=true to send registrants a "facility open" notification. New registrants are notified on the next cycle.
Additional improvements
- Late registrants and new sign-ups always receive notifications - per-registrant delivery is tracked independently of visitor creation
- Events processed until end time so registrants who sign up while an event is in progress still receive access
- Admin portal cards show an Event, Event (shared PIN), or Event (door unlock) badge to distinguish from reservations
- Detail field label shows Event instead of Court on event cards
- Email label correctly shows Event: instead of Court: for events
New environment variables
EVENT_ACCESS_MODE=pin_individual
EVENT_ACCESS_BUFFER_MINUTES=30
EVENT_UNLOCK_NOTIFY=false
Add these to Railway → Variables → Raw Editor.
New CourtReserve API role required
Enable EventRegistrationReport → Read on your API key: CourtReserve Admin → Settings → API Access
Files changed
index.jsenv.exampledocs/configuration.md
How to update
Replace all three files in your GitHub repo — Railway redeploys automatically.
Verified
✅ pin_individual — confirmed working in production
✅ pin_shared — confirmed working in production including late registrants
✅ unlock — confirmed working in production
v1.1.0 - Configurable PIN mode
What's new in v1.1.0
🔑 New feature — configurable PIN mode
A new PIN_MODE environment variable controls how door PINs are generated:
| Mode | Behavior |
|---|---|
random |
New random PIN generated each reservation (default, existing behavior) |
static |
Member's CourtReserve OrganizationMemberId used as their PIN |
Static mode benefits:
- Members learn their PIN once and reuse it for every future reservation
- No need to check email — members always know their PIN
- Staff can look up any member's PIN directly from their CourtReserve profile
- No PIN recovery needed
Static mode requirement:
UniFi Access PIN mode must be set to Variable Length
Access → Settings → General → PIN → Variable Length
New Railway variable:
PIN_MODE=random
Leave as random to keep existing behavior unchanged.
Files changed
index.jsenv.exampledocs/configuration.md
How to update
Replace all three files in your GitHub repo — Railway redeploys automatically.
Verified
✅ Both modes confirmed working in production
v1.0.2 - SMS message improvements
What's new in v1.0.2
📱 SMS message improvements
Complete rewrite of the SMS template for clarity and correctness:
- Club name is now fully dynamic from
BRAND_CLUB_NAME— no hardcoded text - Club name and subject on separate lines for readability with longer names
- Clear instruction to use the PIN at the front door keypad
- States exactly how early the PIN activates based on
ACCESS_BUFFER_MINUTES - Clarifies the PIN is valid for this reservation only
- Removed STOP opt-out line to avoid member confusion on transactional messages
New SMS format:
Your Club Name
Your Door Access PIN
PIN: 67203419
Court: Court 1
Reservation: Mar 15, 12:00 PM
Enter this PIN at the front door keypad. Your PIN is
active 30 minutes before your reservation and is valid
for this reservation only.
Do not share this PIN.
🧹 Documentation cleanup
- Updated SMS example in
docs/email-setup.mdto use generic placeholder values
Files changed
index.jsdocs/email-setup.md
How to update
Replace both files in your GitHub repo — Railway redeploys automatically.
Verified
✅ Confirmed working in production
v1.0.1 - Visitor cleanup fix
What changed
🐛 Bug fix — orphaned UniFi visitor cleanup
Previously, cleanup only ran against entries tracked in state.json. If Railway restarted and cleared /tmp/state.json, orphaned visitor records would remain in UniFi indefinitely — PINs never expired.
Cleanup now runs two passes every minute:
| Pass | Method | Catches |
|---|---|---|
| Pass 1 | Reads state.json |
Visitors CourtPin created in this session |
| Pass 2 | Queries UniFi directly | Orphaned visitors from any previous session |
You will see this in Railway logs when Pass 2 fires:
[INFO] Cleaning up orphaned UniFi visitor {"visitorId":"..."}
How to update
Replace index.js in your GitHub repo — Railway redeploys automatically.
Verified
✅ Confirmed working in production
v1.0.0 - Initial release
CourtPin v1.0.0
First public release.
Features
- Automatic PIN delivery via email and optional SMS
- Time-limited UniFi Access visitor creation
- Branded emails with club name and colors
- Mobile-friendly admin portal for PIN lookup and resend
- Multi-player reservation support
- Auto-cleanup of expired visitor records
- Supports Railway, Raspberry Pi, Docker, NAS, VPS, Windows
Requirements
- CourtReserve account with API access
- UniFi Access 1.9.1+
- Resend account (free) or SMTP