Blitz splits cleanly across a Next.js application that athletes log into, a Postgres database in Supabase that every part of the system reads and mutates, an n8n automation layer that runs the outbound work in the background, and a stack of integrations that handle email, search, and AI.
Athlete onboarding, dashboards, deal pipeline, and a unified inbox for replies. App Router, React 19, server components, middleware-enforced auth. Deployed on Vercel.
Postgres + Auth + Storage. Seven tables hold the entire system state: athletes, their deliverables and preferences, target companies, contacts, campaigns, and deals. Row-level security isolates every athlete's data.
Runs the outbound work asynchronously: company discovery, contact scraping, personalization, send. Connects to the same Supabase database the app reads from. Trigger model is per-athlete — when an athlete activates outreach, n8n picks them up.
Resend for transactional and outbound email with proper deliverability. Anthropic Claude for the personalization step. Mapbox for visualizing target companies geographically. Google APIs for inbox sync.
From the moment an athlete finishes onboarding to the moment a brand replies and the conversation hands back to the human. Two parallel halves of the system — the application the athlete sees, and the automation that runs in the background — meeting in the middle at a single Postgres database.
The shared database is the integration. The Next.js app and the n8n workflows never call each other directly — they both read and mutate Postgres. Athletes can edit their preferences mid-campaign and the next run picks up the change. n8n can write a new contact and the athlete sees it in /brands a moment later. Decoupling falls out for free.
The database is the spine of the whole system. Every page in the app queries it, every n8n step reads or writes to it, every deal moves through it. RLS policies at the row level mean each athlete only ever sees their own data — not enforced by application code, but by the database itself.
The fields shown are reconstructed from PROJECT.md and the application's middleware-protected route surface, not the live migration files. Field names follow the conventions visible in the codebase. Treat this as the architectural shape — the deployed schema may have drifted in details.
Four asynchronous stages, each one writing back to the same Postgres database. n8n runs the whole sequence per-athlete, idempotent, with each stage operating on whatever rows are due for it. New athletes get their first run within minutes of activating. Existing athletes get re-evaluated on a schedule.
Claude does exactly one thing in Blitz: write a single email per prospect that doesn't sound like a template. That's it. The rest of the system is deterministic Postgres queries and n8n flows. Keeping the AI surface narrow is the point — fewer places for an LLM mistake to break the system, more concentrated impact where it matters.
The prompt fuses the athlete's profile (school, sport, year, deliverables) with the prospect's profile (company, industry, role) and asks for a subject line + body in the athlete's voice. Strict rules: no hype words, no “I'm reaching out because”, no fake compliments. Output drops directly into the campaign row.
The personalization payload — what the model saw and what it returned — gets stored on the campaign row so the system can attribute reply rates to specific prompt variants later.
Discovery and scraping are deliberately rule-based. Match scores come from comparing the athlete's preferences against the company's metadata — distance, industry overlap, exclusion list. No semantic match, no embedding similarity. That's a future feature; for the MVP, deterministic targeting is more debuggable, cheaper to run, and lets the personalization step be the moment the AI earns its keep.
If the personalization fails, the row is skipped and surfaced in the admin panel for manual review. The campaign never sends template-y junk.
Twelve dependencies that actually do something. The picks were calibrated for a solo build — minimum infrastructure, maximum leverage from managed services, and an automation layer that doesn't require running a server.
Decisions that don't show up on a feature list, but that separate “I bolted some tools together” from “I designed this”.
The Next.js app and the n8n flows never call each other directly. Both read and write Postgres. The athlete edits a preference in the app, the next n8n run picks it up. n8n discovers a company, the athlete sees it in /brands a second later. No internal API to version, no webhook contracts to maintain.
Athlete data isolation is enforced by Postgres row-level security policies, not by application code that filters by athlete_id. A bug in a Next.js route handler can't leak another athlete's data — the database itself refuses to return rows that don't belong to the requesting session.
Match scores compare the athlete's preferences (industries, geo radius, size range, exclusion list) against company metadata. No embeddings, no semantic similarity. Cheaper, debuggable, and lets the AI step concentrate where it matters: writing the email.
Every campaign row keeps the JSON of what was sent to Claude and what came back. This makes it possible to run prompt-variant analysis later — which prompt template correlates with higher reply rates — without re-running anything.
Once a brand replies, the conversation moves to the athlete's /inbox and the automation stops. Blitz doesn't try to negotiate deals — it gets the door open. The athlete closes from there, the platform tracks the deal, and commission gets deducted on payout. Clean separation between “machine work” and “human work”.
The middleware checks NEXT_PUBLIC_ADMIN_EMAILS for admin routes. For an MVP with a single admin (me) it's the right tradeoff: zero schema, no role-management UI, instant. When the user count justifies a real role table, the middleware swap is one PR.
Blitz is the athlete-side mirror of Georgia. Same problem at the core — running personalized outreach at scale — different stack, different audience, different scaffolding around the AI.
Or return to the index for contact and other work.