This is a submission for Weekend Challenge: Passion Edition
What I Built
Called It! is a social football prediction platform. You predict match outcomes before kickoff, earn points based on how accurate you were, and compete with friends. No squad management, no transfers, no budgets. Just your ability to read a game.
The problem is something I've felt for years. I have a group chat where we all throw out predictions before big matches. Someone says 2-1 with a specific goalscorer. Someone else says 3-0. Then the game ends, the chat scrolls past, and nobody remembers who actually got it right. There is no scoreboard for "I told you so."
Called It! gives that dynamic a scoreboard. Every prediction is timestamped, locked at kickoff, and scored automatically against real match data. Get the exact score and the first goalscorer right in the same prediction and you earn a Called It card, a kind of digital receipt you can share. Those are genuinely hard to get!
There is something about prediction that overlaps with the same part of your brain that betting does. You pick a score, you commit to a goalscorer, and for 90 minutes you are wired to every pass and shot in a way you would not be otherwise. The difference is there is no money on the line; it's not a harmful habit and there's no loss. You are not chasing a payout. You are chasing a spot above your mate on a leaderboard, or a Called It card you can throw in the group chat, which makes the whole concept very healthy! The incentive is social, not financial. That sounds like a small distinction but it changes the whole feel. Nobody loses anything. Nobody chases losses. Worst case, you got a match wrong and you drop a few ranking spots. Best case, you called a 3-1 with the exact scorer and everyone knows it.
I built this over a weekend. It has live sports data from football-data.org, automated result processing, friend requests, and leaderboards. It is deployed and functional. I kept the scope tight because weekend hackathons punish ambition that drifts too far from what one person can actually ship.
Demo
The live app is deployed at https://called-it-two.vercel.app.
You can create an account, pick a favorite team, predict upcoming fixtures, and see your ranking and points update as results come in. If you nail a prediction completely, the Called It card page is public and shareable.
Code
The full source is at https://github.com/Raiden505/called-it.
Stack: Next.js App Router with TypeScript, Tailwind CSS for styling, Supabase for auth and Postgres, and the football-data.org API v4 for live match data.
How I Built It
Architecture choice: a modular monolith
The most consequential decision I made early on was staying with a single Next.js app instead of splitting into separate services. A weekend build with separate frontend and backend deployments would have eaten half the time just in configuration and debugging. Instead, everything lives in one codebase with server actions and route handlers handling the backend work. The domain logic sits under lib/ in bounded modules (scoring, cards, leaderboards, sports sync), each with its own tests.
This is not the architecture I would recommend for a team of ten, but for a solo weekend build it meant I spent time on features instead of infrastructure.
Scoring engine
The scoring is pure math, no AI involved. Correct outcome is worth 3 points. Matching the exact goal difference adds 2. Getting the score exactly right adds 3. Naming the first goalscorer is worth 4. You can attach a confidence multiplier (1x, 2x, or 3x) to any prediction, but you only get three of each multiplier per tournament round, enforced atomically through a Postgres RPC.
A 0-0 match with "no goalscorer" predicted correctly still earns the full 4 points for first goalscorer. Own goals do not count. Knockout matches use regulation-time scoring only.
The engine is 86 lines of TypeScript and was the first thing I wrote tests for. When a result gets corrected (because real match data is occasionally wrong), the entire scoring pipeline reruns idempotently. Predictions get rescored, Called It cards get reissued or revoked, and deduplication keys prevent double notifications. The atomic RPC that handles this is one of the most carefully designed pieces of the whole app.
Security: RLS on everything
Every user-facing table has Row Level Security enabled. Predictions are hidden from other users before kickoff. Friends can see them after the match starts. Clients cannot insert Called It cards or modify scoring fields. The sports sync tables are locked down so only the service role can touch them. The cron endpoint is protected by a shared secret.
I set this up early because retrofitting RLS is a nightmare. It paid off almost immediately when I caught a bug where a raw Supabase query in the browser client would have leaked prediction data if the RLS policy had not blocked it.
Live data pipeline
The football-data.org integration was the trickiest part. Their API v4 has rate limits (10 requests per minute on the free tier), and match data changes state over time. A fixture goes from scheduled to live to finished, and even after finishing, the result can be corrected.
I built a due-work system that runs once per minute via Supabase Cron. Each run picks up to 8 fixtures that are due for a refresh, processes them in batches of 4, and prioritizes finished-but-unconfirmed results. Results go through a two-observation confirmation gate: the system reads the match result twice, 90 seconds apart, and only scores predictions when both observations produce the same stable hash. This catches the case where football-data.org shows a provisional score that gets corrected minutes later.
The whole pipeline is gated behind a SPORTS_SYNC_ENABLED flag so I could deploy it disabled and verify behaviour in dry-run mode before turning on live writes.
The dark theme
The UI uses a dark "match-night" palette. Near-black backgrounds, acid-lime accents for calls to action and verification badges, and a condensed typography stack (Bahnschrift Condensed, Arial Narrow, Impact for display, Trebuchet MS and Segoe UI for body). On desktop there is a 248px side navigation rail. On mobile it collapses to a bottom tab bar.
I spent more time on the Called It card visuals than I want to admit. Each card renders as a football match ticket with a lime verification stripe down one edge. The public card page shows the exact prediction, the rarity, and share actions (copy link, native share, SVG download). It looks like something you would actually want to post in a group chat.
What's next
The initial release is scoped to a single tournament, but the match data pipeline was built to handle any football competition. Adding the Premier League or the Champions League is really just a configuration change and another season of seeded data. The hardest part was building the sync infrastructure, not the per-competition logic.
Event-by-event prediction is the feature I most want to add. Guessing the minute of the first goal, the number of corners, whether there will be a red card. That kind of granularity turns a 90-minute match into a series of smaller moments to care about, which is where prediction gets genuinely fun.
Top comments (0)