The scorecard problem League Handicap couldn't reach
League Handicap automated the handicap math for course administrators. Attest goes after the step before it: the paper scorecard, and the person retyping it into Golf Genius by hand.

Where this stands
As of August 2026, Attest exists as a working Flutter app, admin dashboard, API, and Golf Genius extension with a signed Android release build, and active development has been paused since May while League Handicap gets the hours.
League Handicap solved the calculation. A course uploads a file of scores, the platform applies each league’s rules, and handicaps come out the other end. But that file has to exist first, and where it comes from is a golf assistant at a keyboard.
Discovery
The rest of the week runs the way league golf always has. Members play. Someone in the group keeps the card. Players sign each other's cards, which is how league golf has verified scores for as long as there have been leagues. The cards come back to the pro shop in a pile. Then somebody types.
The obvious fix is an API. Golf Genius has one, and its read side is fine: seasons, events, rosters, rounds, courses, divisions, players. There is no endpoint for posting a score. Score entry happens through the Golf Genius app or the manager web interface and nowhere else, and Josh's read is that this is deliberate. Golf Genius is the system of record for tournament management and USGA posting, and it isn't going anywhere. Whatever Attest did, it had to feed Golf Genius rather than compete with it.
The rest of the constraints came from the course. League golfers skew 45 to 70 and up, and the design target is members who are not tech-forward. Cell coverage on a course is notoriously bad. And in a league's first week, maybe five of forty members will have the app at all, so any verification scheme that requires both players to be running it fails on the day it matters most.
Key Takeaway
The paper scorecard isn't really the problem. The retyping is. Once the build was aimed at that, the question became how a number gets from a golfer's head into Golf Genius with a witness attached and nobody transcribing it in between.
What We Built
Attest is designed to take a score three ways and land all of them in the same place. One of the three runs today: live scoring, one hole per screen, plus and minus buttons around a default of par, swipe to move on. The other two are specified and not yet wired — photographing a finished paper card and having a model parse it into hole-by-hole scores, and an admin entering a card on behalf of a member who has no intention of installing anything.
Verification replaces the signature, and the version the product is named for is still on the drawing board. The design is a Bluetooth handshake: the scorer taps Verify, their phone broadcasts a short-lived token, the partner’s phone picks it up, pulls the scorecard from its own local database, and the partner approves. Forty bytes out, sixty back, with the scorecard itself never going over the air. The screen is built and the byte-level protocol is specified; the Bluetooth transport underneath it is not wired yet. What the database does record today is which method produced an attestation, so an admin reviewing a season can tell an in-person signature from a remote approval.
That works in a league where everyone has the app, which no league is on week one. So the launch method is deliberately dumber: the verifier signs on the scorer's phone and types their name, the same choreography as passing a paper card across the cart. One phone per group is enough to start. Bluetooth and push-based remote verification turn on as adoption grows.
Then the scores have to reach Golf Genius, and there's no API for that. The last mile is a Chrome side-panel extension. The admin exports verified scorecards from Attest as an XLSX, opens the Golf Genius scorecards page they already have access to, and drags the file into the panel. The extension reads the round roster off the page, matches each row by Golf Genius handle, shows every match and mismatch before anything is submitted, and posts through the same form the admin would otherwise fill in by hand. It runs inside the admin's own browser session. No credentials are stored and no third party touches the course's Golf Genius account.
Underneath all of it: a Flutter app for iOS and Android, a Next.js admin dashboard, a marketing site, and a tRPC API over Postgres. Offline-first through PowerSync, because the back nine has no signal and the app can't stall mid-round waiting for a network. There's an Apple Watch companion in prototype, and a signed Android release build sitting one Play Console submission away from internal testing.
Picking It Back Up
Attest is paused. League Handicap is the product with paying courses on it, and that's where the hours have gone. Resuming Attest means finishing a short list, not starting over.
- Store submission. The Android release build is signed and the domain is owned. What's left is the Play Console listing, the data safety questionnaire, and a privacy route on attest.golf.
- Parsing accuracy on real cards. The photo parser needs a test set of actual scorecards from several courses, in both single-player and group formats, before it's trusted in a live league.
- Phone-based identity claim. Members with no email on file can't finish the OTP claim flow yet. The Twilio A2P campaign was awaiting approval when work stopped.
- One friendly league. The plan was always a small launch with the whole workflow intact end to end, entry through export, rather than a partial rollout.
- Pricing. Attest is meant to be sold per league with members free. A per-league figure is modeled internally but has not been tested against a real course.
None of that is research work. It's a finish list against a codebase that runs today. When a course asks for it, or when League Handicap's roadmap walks into the same paper card, the work is there to pick back up.
Does your workflow end with someone retyping it?
That's usually where the errors and the hours are hiding. Let's take a look at yours.
Schedule a Conversation