Platform Overview
Founders Fuel Academy is a self-hosted community + online course platform built specifically for Indian entrepreneurs. It combines a gated community feed, a full course curriculum, a members-only resource vault, live events, and a Razorpay-powered subscription system — all in a single Next.js 16 application with no separate backend.
Stack
Next.js 16, TypeScript, Tailwind CSS
Database
PostgreSQL via Prisma 5
Payments
Razorpay Subscriptions
Resend (transactional + bulk)
Storage
Vercel Blob
Hosting
Vercel (Serverless + Edge)
Auth
NextAuth.js (Google OAuth + Email)
AI
Google Gemini API (newsletter)
Domain: https://foundersfuelacademy.com
Architecture
Routing Structure
app/
(auth)/ — login, signup (no header/nav layout)
(public)/ — landing, about, vision, subscribe, unsubscribe, manual
(platform)/ — all authenticated pages (PlatformLayout wraps these)
admin/ — admin-only dashboard & tools
classroom/ — course list + lesson player
community/ — social feed + post detail
resources/ — members-only vault
members/ — directory + profile pages
messages/ — DM inbox + thread
free/ — public-facing free resources
events/calendar/ — event calendar
api/ — all API routes (Next.js Route Handlers)
Key Conventions
- • All times stored in UTC, displayed in IST (Asia/Kolkata).
- • Razorpay webhooks are the source of truth for subscription state.
- • Server components by default;
"use client"only when state/effects needed. - • All API routes return
{ data, error }shape. - • Monetary amounts stored in paise (×100 for Razorpay).
- •
getRazorpay()andgetResend()are lazy factories — safe to import at build time. - • Middleware at
middleware.tsgates/platform/*routes — checks session cookie. - • Gamification points are calculated on-write, not in batch.
Access Control Tiers
| Tier | Who | Access |
|---|---|---|
| Public | Anyone | Landing, about, vision, subscribe, login, signup |
| Free | Signed-in, no active sub | Community (read), free resources, preview lesson, leaderboard, members directory |
| Paid | Active Razorpay subscription | Everything in Free + full classroom, resource vault, DMs, events |
| Admin | role = "admin" in DB | Everything + admin panel, member management, course editing, email tools |
Authentication
Powered by NextAuth.js. Two providers are configured: Google OAuth and Credentials (email + bcrypt password).
Login Flow
- User visits
/loginand submits email/password or clicks Google. - NextAuth issues a JWT session cookie (
next-auth.session-token). - JWT payload includes:
id, email, role, name, image, gamificationLevel. - Middleware at
middleware.tsreads the cookie — unauthenticated requests redirect to/login. - Admin role is checked server-side via
session.user.role === "admin".
Session Helper
Use getSession() from lib/session.ts in server components (React-cached, avoids N+1 queries). Use auth() from lib/auth.ts directly in API routes.
Subscriptions & Payments
All payments are processed through Razorpay Subscriptions. The platform uses recurring plans (monthly/annual) and the webhook handler is the authoritative source for subscription state.
Checkout Flow
POST /api/subscriptions/create— creates a Razorpay subscription, returnssubscription_id.- Front-end opens the Razorpay checkout modal with the subscription ID.
- On payment success, front-end calls
POST /api/subscriptions/verifywith signature. - Razorpay fires webhook to
POST /api/webhooks/razorpay— this creates/updates theSubscriptionrecord. - Webhook also triggers welcome email, in-app notification, and gamification points.
Coupons
Coupons are managed at Admin → Coupons. Supported types: PERCENT and FLAT. Validate via POST /api/coupons/validate at checkout time. The coupon code is stored on the Subscription record for auditing.
Webhook Events Handled
subscription.activated— mark subscription active, send welcome emailsubscription.charged— renewal confirmedsubscription.cancelled— mark cancelledsubscription.halted— mark expired, send reminderpayment.failed— log failure
Community
The community feed is the social hub. All signed-in users can read; only paid members can post.
Posts
- • Categories: General, Wins, Questions, Resources, Announcements, Events
- • Media: Images, videos, links supported. Uploaded via
/api/posts/uploadto Vercel Blob. - • Pinning & hiding: Admin can pin important posts and hide inappropriate ones.
- • Polls: Posts can include a poll (stored as JSON in
pollData). - • Gamification: +5 pts on post creation; +2 pts per like received.
Comments & Likes
- • Threaded: Comments can have a
parentCommentIdfor reply threads. - • Likes: Works on both posts and comments. A user can only like once per item.
- • Gamification: +3 pts for creating a comment; +1 pt per like received on a comment.
Notifications on Activity
When someone comments on or likes your post, an in-app notification is created. Notification bell in the header shows unread count. Clicking marks them read. The unread-reply badge(orange “Tap to reply”) appears for notifications the user has seen but not yet acted on — tracked in localStorage under notif-actioned.
Classroom
Full LMS with 8 courses, 70+ lessons, module grouping, drip release, and progress tracking.
Content Hierarchy
Course → Module → Lesson
- • Course: Has a cover image, description, access level (free/paid), and sort order.
- • Module: Grouping of lessons within a course. Has a sort order.
- • Lesson: Video URL, Markdown content, action items (JSON array), completion tracking.
Access Gating
- • Lessons with
isFree = trueare accessible to all signed-in users. - • The first lesson of the first published course is always free (preview).
- • All other lessons require an active subscription — shown as
UpgradeWallif not subscribed. - • Admin bypasses all gates.
Progress & View Tracking
- • Completion: Marked via
POST /api/lessons/[id]/complete. Stored inUserLessonProgress. Awards +5 pts; +50 pts on full course completion. - • View tracking:
LessonViewTrackerclient component firesPOST /api/track/lessonon mount. Inserts intolesson_viewstable (used in weekly report). - • Prev / Next lesson navigation is shown at the bottom of each lesson page.
Drip Content
If a course has isDrip = true, lessons are unlocked based on dripDayOffsetrelative to the member's subscription start date. This is enforced in the lesson page access check.
Resource Vault
A members-only file vault for PDFs, templates, videos, links, zip files — anything the admin uploads. Files are stored in Vercel Blob.
Adding Resources (Admin)
- • Admin sees an “Add Resource” button on the Resources page (top-right).
- • Supports single file, multiple files, external links, and video URLs.
- • Multi-file upload: Multiple files are grouped under a Folder Name. Each file is uploaded individually with
?skipNotify=true, then one summary notification email is sent viaPOST /api/resources/notify. - • Single file: Requires Title, Category (folder), Type, and optional description. Sends individual email on upload.
Folder Accordion UI
Resources are grouped by their category field and displayed as collapsible folder cards (ResourceFolderView component). Each folder shows file count and total size. If only one folder exists, it auto-opens. Clicking a file fires a resource view event.
Member Notifications
- • Paid members: Get email with resource title/link to
/resources. - • Free users: Get a teaser email with a link to
/subscribeand pricing CTA.
Free Resources (Public)
Separate from the vault — managed via Admin → Free Resources. These are links/resources visible to all signed-in users at /free. Stored in the FreeResource model.
View Tracking
Every resource click fires POST /api/track/resource. Inserts a row into resource_views table. Used in the weekly analytics report to show engagement trends.
Events & Calendar
Admin can schedule live events (calls, workshops, AMAs) visible on the platform calendar.
Creating & Editing Events
- • Admin creates events via Admin → Events with: title, date/time (IST), duration, category, meeting link, recording URL.
- • Event times are saved in UTC, always displayed in IST (
Asia/Kolkata). - • A double-conversion bug was fixed: the form input is IST → converted once to UTC on save. Never double-convert.
- • Editing: Admin can edit all event fields inline via the event card.
Reminders
- •
POST /api/events/[id]/remindsends email reminders to all paid members. - • Admin always receives a notification copy regardless of event type.
Direct Messaging
1-to-1 direct messaging between members. Available at /messages.
- • Messages stored in
DirectMessagetable (senderId, receiverId, body, isRead). - • Inbox shows all conversations grouped by the other participant.
- • Messages are marked read via
POST /api/messages/readwhen the thread is opened. - • Unread DM count shown in the sidebar navigation badge.
Notifications
In-app notification bell in the platform header. Three visual states:
Unread
Orange background + orange dot. Cleared when bell is opened.
Read
Plain white — user has seen the notification.
Read, not replied
Orange “Tap to reply →” badge. Tracked in localStorage (notif-actioned). Disappears after clicking.
- • Notification types:
comment,reply,mention,like,new_post,direct_message,event_reminder,system - • Created via
createNotification()inlib/notifications.ts. - • Web push (browser notifications) supported via
PushSubscriptionmodel. Subscribe atPOST /api/push/subscribe. - • Daily cron (
/api/cron/notify-unread) emails users about unread notifications they missed.
Gamification
Points-based system with 10 levels, visible on the leaderboard and member profiles.
Points Table
| Action | Points |
|---|---|
| Post created | +5 |
| Like received on post | +2 |
| Like received on comment | +1 |
| Comment created | +3 |
| Lesson completed | +5 |
| Course completed | +50 |
| 7-day login streak | +10 |
Level Thresholds
L1
0 pts
L2
50 pts
L3
150 pts
L4
350 pts
L5
700 pts
L6
1200 pts
L7
2000 pts
L8
3500 pts
L9
5500 pts
L10
8000 pts
Analytics & Tracking
Weekly Report (Friday 9 AM IST)
Auto-generated email report sent every Friday at 9 AM IST via /api/cron/weekly-report. Can also be triggered manually from admin or via GET request with the cron bearer token.
Metrics tracked (this week vs last week):
Alert thresholds:
- • 🔴 Zero new signups
- • 🔴 Signups dropped ≥ 25% week-over-week
- • 🔴 No new paid conversions (when free users exist)
- • 🔴 Paid conversions dropped ≥ 30%
- • 🟡 Community engagement dropped ≥ 25%
- • 🟡 Lesson completions dropped ≥ 25%
- • 🔴 3+ subscriptions cancelled/expired
View Tracking Tables
Two raw SQL tables (not Prisma-migrated — created via GET /api/admin/run-migrations):
- •
lesson_views (id, lesson_id, user_id, viewed_at)— logged every time a lesson page loads - •
resource_views (id, resource_id, user_id, viewed_at)— logged on resource click
Funnel Events
The UserEvent model tracks conversion funnel milestones:signup, subscribe_page_view, checkout_started, subscription_created. Visible in the Admin Analytics dashboard.
Email & Newsletter
All transactional and marketing emails are sent via Resend. The from address is configured via RESEND_FROM.
Email Types
| Trigger | |
|---|---|
| Welcome email | Subscription activated webhook |
| Subscription reminder (7d, 1d, 0d) | Daily cron /api/cron/subscription-reminders |
| Resource notification (paid) | New resource uploaded |
| Resource teaser (free users) | New resource uploaded |
| Event reminder | Admin clicks Remind on event |
| Weekly analytics report | Friday 9 AM IST cron |
| Free tier nurture sequence | Monday cron, 14d after signup |
| Conversion emails | Daily cron, targets free users |
| Unread notifications digest | Daily 6:30 AM IST cron |
| Newsletter broadcast | Admin sends via Admin → Email |
| Bug report confirmation | User submits bug report |
Subscriber Management
Newsletter subscribers (separate from members) are stored in EmailSubscriber. Managed at Admin → Subscribers with bulk import/export, UTM tracking, and unsubscribe handling.
Admin Panel
Accessible at /admin — only for users with role = "admin" in the database.
Dashboard
/admin
Overview: total members, revenue, recent activity
Analytics
/admin/analytics
Funnel metrics, signups, conversions, engagement trends
Members
/admin/members (+ /[userId])
List all users, edit roles, view subscription status
Courses
/admin/courses (+ /[courseId])
Create/edit courses, modules, lessons. Toggle published state.
Resources
/admin/resources
Manage paid vault resources. Edit titles, categories, delete.
Coupons
/admin/coupons
Create PERCENT or FLAT coupons with usage limits and expiry
Events
/admin/events
Create and edit live events with reminders
Subscribers
/admin/subscribers
Email subscriber list, bulk import CSV, send campaigns
Email / Newsletter
/admin/email
Compose newsletters, AI drafting, send broadcasts
Cron Jobs
Configured in vercel.json. All schedules are UTC. Protected by Authorization: Bearer CRON_SECRET.
| Job | Schedule (UTC) | IST Time | Purpose |
|---|---|---|---|
| /api/cron/subscription-reminders | 30 2 * * * | 8:00 AM daily | Subscription expiry reminders + grace-period kicks |
| /api/cron/free-tier-nurture | 0 4 * * 1 | 9:30 AM Monday | Nurture emails to free users who haven't converted |
| /api/cron/newsletter | 30 3 * * * | 9:00 AM daily | Scheduled newsletter digest sending |
| /api/cron/notify-unread | 30 6 * * * | 12:00 PM daily | Email users about unread in-app notifications |
| /api/cron/conversion-emails | 30 1 * * * | 7:00 AM daily | Conversion funnel emails to free users |
| /api/cron/weekly-report | 30 3 * * 5 | 9:00 AM Friday | Weekly analytics report email to admin |
API Reference
All routes are in app/api/. All POST/PATCH bodies are JSON unless noted.
Authentication
| POST | /api/auth/register | Create new user account (email + password) | public |
| GET | /api/auth/check-email | Check if an email is already registered | public |
| POST | /api/auth/logout | Destroy session cookie | user |
| POST | /api/auth/clear | Clear stale auth cookies | public |
Community
| GET | /api/posts | List community posts (paginated, filterable by category) | user |
| POST | /api/posts | Create a new post | paid |
| PATCH | /api/posts/[id] | Edit post (author or admin) | user |
| DELETE | /api/posts/[id] | Delete post (author or admin) | user |
| POST | /api/posts/upload | Upload media for a post → Vercel Blob | paid |
| POST | /api/posts/[postId]/vote | Toggle like on a post | user |
| GET | /api/posts/[postId]/voters | List users who liked a post | user |
| GET | /api/comments | List comments for a post | user |
| POST | /api/comments | Create a comment (or reply) | paid |
| DELETE | /api/comments/[id] | Delete comment (author or admin) | user |
| POST | /api/likes | Toggle like on a comment | user |
Resources
| GET | /api/resources | List paid resources (grouped by category) | paid |
| POST | /api/resources/upload | Upload resource file or link to vault. ?skipNotify=true suppresses emails | admin |
| POST | /api/resources/notify | Send batch notification after multi-file upload | admin |
| POST | /api/track/resource | Record a resource view event (fire-and-forget) | user |
| GET | /api/admin/free-resources | List free resources | admin |
| POST | /api/admin/free-resources | Create free resource | admin |
| DELETE | /api/admin/free-resources/[id] | Delete free resource | admin |
Classroom
| GET | /api/courses/progress | Get completion % per course for current user | user |
| POST | /api/track/lesson | Record a lesson view event (fire-and-forget) | user |
| PATCH | /api/admin/lessons/[lessonId] | Update lesson title, video, content, action items | admin |
Subscriptions & Payments
| POST | /api/subscriptions/create | Create Razorpay subscription. Returns subscription_id. | user |
| POST | /api/subscriptions/verify | Verify Razorpay payment signature after checkout | user |
| POST | /api/subscriptions/cancel | Cancel current subscription | user |
| POST | /api/coupons/validate | Validate coupon code. Returns discount amount. | user |
| POST | /api/webhooks/razorpay | Razorpay webhook handler (HMAC-verified) | public |
Events
| GET | /api/events/upcoming | Get next 5 upcoming events | user |
| GET | /api/events/all | Get all events for calendar view | user |
| POST | /api/events | Create event | admin |
| PATCH | /api/events/[id] | Edit event fields | admin |
| DELETE | /api/events/[id] | Delete event | admin |
| POST | /api/events/[id]/remind | Send reminder email to all paid members | admin |
Messages
| GET | /api/messages | Get DM thread between current user and ?userId= | user |
| POST | /api/messages | Send a direct message | paid |
| POST | /api/messages/read | Mark messages in thread as read | user |
Admin Utilities
| GET | /api/admin/run-migrations | Create resource_views + lesson_views tables (idempotent) | admin |
| GET | /api/admin/analytics | Full analytics dashboard data | admin |
| GET | /api/admin/members | List all users with subscription info | admin |
| PATCH | /api/admin/members/[userId] | Update user role, subscription override | admin |
| GET | /api/admin/subscribers | List all email subscribers | admin |
| POST | /api/admin/subscribers/import | Bulk CSV import of subscribers | admin |
| POST | /api/admin/broadcast | Send broadcast email to a segment | admin |
| POST | /api/admin/newsletter/draft | Save/update newsletter draft | admin |
| POST | /api/admin/newsletter/send-draft | Send draft newsletter to all subscribers | admin |
| POST | /api/admin/newsletter/ai-compose | AI-generate newsletter (Gemini API) | admin |
| GET | /api/cron/weekly-report | Trigger weekly report manually. Bearer CRON_SECRET or admin session. | admin |
| POST | /api/admin/test-email | Send diagnostic test email to admin address | admin |
| GET | /api/heartbeat | Update user's online/lastActive timestamp | user |
Environment Variables
Set in Vercel Dashboard → Project → Settings → Environment Variables. Never commit to git.
| Variable | Purpose | Required |
|---|---|---|
| DATABASE_URL | PostgreSQL connection string (Prisma) | required |
| NEXTAUTH_SECRET | JWT signing secret (min 32 chars) | required |
| NEXTAUTH_URL | Full app URL (e.g. https://foundersfuelacademy.com) | required |
| NEXT_PUBLIC_APP_URL | Public app URL (used in emails/links) | required |
| GOOGLE_CLIENT_ID | Google OAuth client ID | required |
| GOOGLE_CLIENT_SECRET | Google OAuth client secret | required |
| RAZORPAY_KEY_ID | Razorpay API key ID | required |
| RAZORPAY_KEY_SECRET | Razorpay API secret | required |
| RAZORPAY_WEBHOOK_SECRET | Webhook signature secret | required |
| RAZORPAY_PLAN_ID | Default Razorpay subscription plan ID | required |
| RESEND_API_KEY | Resend email API key | required |
| RESEND_FROM | From address (e.g. team@foundersfuelacademy.com) | required |
| ADMIN_EMAIL | Admin email for reports/alerts | required |
| BLOB_READ_WRITE_TOKEN | Vercel Blob storage token | required |
| CRON_SECRET | Bearer token to authenticate cron job calls | required |
| GEMINI_API_KEY | Google Gemini API key for AI newsletter composition | required |
| VAPID_PUBLIC_KEY | Web push VAPID public key | optional |
| VAPID_PRIVATE_KEY | Web push VAPID private key | optional |
| GMAIL_CLIENT_ID | Gmail OAuth client ID (newsletter sending) | optional |
| GMAIL_CLIENT_SECRET | Gmail OAuth client secret | optional |
Database Schema
Managed by Prisma 5. Source of truth: prisma/schema.prisma. Run npm run db:generate after schema changes, npm run db:migrate to apply.
Note: resource_views and lesson_views are created via raw SQL through/api/admin/run-migrations — not via Prisma migrations. Run this endpoint once after deploy to production.
User
Core user account + gamification + online status
Account
OAuth provider accounts (NextAuth)
Session
Active user sessions
Subscription
Razorpay subscription records
Coupon
Discount codes
Post
Community feed posts
Comment
Threaded post comments
Like
Post and comment likes
Course
Course metadata
Module
Course section grouping
Lesson
Individual lesson content
UserLessonProgress
Lesson completion tracking
Event
Scheduled live events
GamificationLog
Points audit trail
DirectMessage
1-to-1 messages
UserEvent
Analytics funnel events
FreeResource
Public free resources
PaidResource
Members vault resources
Notification
In-app notifications
PushSubscription
Browser push tokens
EmailSubscriber
Newsletter subscribers
SystemSetting
Key-value config store
ResourceView
Resource view tracking (raw SQL)
LessonView
Lesson view tracking (raw SQL)
Deployment
Deploying to Vercel
- Ensure all env vars are set in Vercel project settings.
- Run
npm run buildlocally first to catch TypeScript errors. - Deploy:
vercel --prod - After first deploy (or after adding new models): call
GET /api/admin/run-migrationsto create raw SQL tables.
Database Migrations
- • Schema changes: edit
prisma/schema.prisma, runnpm run db:migrate(requiresDATABASE_URL). - • View data:
npm run db:studio— opens Prisma Studio GUI. - • Seed courses:
npm run db:seed— loads 8 courses and 70+ lessons.
Post-Deploy Checklist
- □Verify Razorpay webhook URL is set to https://foundersfuelacademy.com/api/webhooks/razorpay
- □Run GET /api/admin/run-migrations to create lesson_views + resource_views tables
- □Send a test email via POST /api/admin/test-email
- □Confirm cron jobs are active in Vercel Dashboard → Settings → Cron Jobs
- □Check admin panel loads at /admin
Founders Fuel Academy Platform Manual · Updated May 2026 ·Admin Panel