A production-ready, open-source AI SaaS boilerplate built around a real product: an AI document summarizer. Auth, subscriptions, usage metering, and the AI feature are all wired up so you can fork it and ship your own SaaS.
Most "SaaS starters" stop at a login page. distill is end-to-end: GitHub auth via NextAuth, Stripe subscription checkout and webhooks, per-plan usage limits enforced server-side, a working OpenAI feature, and GDPR-friendly account deletion. It is the boilerplate that would have saved you weeks.
- Next.js 14 App Router + TypeScript
- NextAuth (database sessions, Prisma adapter)
- Prisma + PostgreSQL
- Stripe subscriptions (Checkout + webhooks)
- OpenAI for summarization
- Tailwind CSS
- Marketing landing + usage-based pricing page
- GitHub sign-in
- Dashboard with live monthly usage meter
- Server-side plan limits with upgrade prompts (
free/pro/team) - Stripe Checkout and a webhook that keeps plan state in sync
- Account + data deletion endpoint (privacy compliance)
- Centralized, validated env config in
lib/env.ts - Single pricing source of truth in
lib/pricing.ts
pnpm install # runs `prisma generate`
cp .env.example .env.local # fill in DATABASE_URL, auth, OpenAI, Stripe
pnpm db:push # create the schema in your database
pnpm dev- Database — any PostgreSQL URL (Neon/Supabase/local).
- GitHub OAuth app — set
GITHUB_ID/GITHUB_SECRET. - OpenAI — set
OPENAI_API_KEY. - Stripe — set
STRIPE_SECRET_KEY(prefer a restricted key,rk_...) and point a webhook at/api/stripe/webhookwithSTRIPE_WEBHOOK_SECRET. You never paste price IDs — see below.
All variables are documented in .env.example.
Plans are defined in code in lib/pricing.ts — name, amount, and a stable lookupKey. To provision them in Stripe, run:
pnpm stripe:syncThis idempotently creates/updates the Products and Prices in whatever account STRIPE_SECRET_KEY belongs to (so run it with your key), tagging each price with its lookupKey. At runtime the app resolves the live price ID by lookupKey via lib/stripe-prices.ts — no price IDs in env or code. Changing a price is just editing lib/pricing.ts and re-running the sync.
Recurring subscriptions. Pick an ultra-narrow niche (e.g. "summarize leases for realtors") and the free tier becomes your funnel. Pricing lives in lib/pricing.ts.
pnpm dev/pnpm build/pnpm startpnpm lint/pnpm typecheckpnpm db:push— push the Prisma schemapnpm stripe:sync— provision Stripe products/prices fromlib/pricing.ts
MIT — see LICENSE.