A Next.js application to track founder mood during their startup journey. Sends daily mood check emails (morning and afternoon) and visualizes mood trends over time.
- API Token Authentication: Secure access to company-specific mood data
- Founder Management: Add/remove founders who will receive mood check emails
- Automated Emails: Sends mood check emails at 6 AM and 10 PM via Gmail
- AI-Powered Sentiment Analysis: Uses OpenRouter (GPT-4o-mini) to categorize mood responses (0-5 scale)
- Interactive Dashboard: View mood trends with All, Weekly, or Monthly views
- Real-time Updates: Dashboard auto-refreshes every 30 seconds
- Click to View Replies: Click on any entry to see the exact email response
- Beautiful Visualizations: Chart mood data over time with Recharts
- Frontend: Next.js 15, React 18, TypeScript, Tailwind CSS
- Backend: Next.js API Routes
- Database: PostgreSQL with Prisma ORM
- Email (Send): Gmail SMTP via Nodemailer
- Email (Receive): Gmail IMAP polling
- AI: OpenRouter with Vercel AI SDK (GPT-4o-mini with low reasoning)
- Deployment: Vercel (with Vercel Postgres)
pnpm install- Go to Google Account Security
- Under "How you sign in to Google", click on 2-Step Verification
- Follow the steps to enable 2FA if not already enabled (required for app passwords)
- Go to Google App Passwords
- Note: You must have 2FA enabled first, otherwise this page won't be available
- You may need to sign in again
- At the bottom, under "App passwords":
- Select app: Choose Mail
- Select device: Choose Other (Custom name)
- Enter a name like "Mood Tracker"
- Click Generate
- Google will show you a 16-character password (format:
xxxx xxxx xxxx xxxx) - Copy this password immediately - you won't be able to see it again
- Use this password (without spaces) as your
GMAIL_APP_PASSWORDin.env
Important: Use the app password, NOT your regular Gmail password!
- Go to Gmail Settings
- Click on Forwarding and POP/IMAP tab
- Under "IMAP access", select Enable IMAP
- Click Save Changes at the bottom
Create a .env file based on .env.example:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mood"
# Gmail credentials
GMAIL_USER="your-email@gmail.com"
GMAIL_APP_PASSWORD="your 16 char app password"
# OpenRouter for AI mood categorization
OPENROUTER_API_KEY="sk-or-your_api_key_here"
# Cron secret for securing cron endpoints
CRON_SECRET="your_random_secret_here"
# Base URL
NEXT_PUBLIC_BASE_URL="http://localhost:3002"Run database migrations:
pnpm prisma migrate dev --name initGenerate Prisma client:
pnpm prisma generateRun the seed script to create an initial API key and founder:
pnpm prisma db seedThis creates:
- API Token:
desplega-dev-token-12345 - Company: desplega.ai
- Founder: Taras (t@desplega.ai)
You can edit prisma/seed.ts to customize these values.
- Sign up at OpenRouter
- Get your API key
- Add it to your
.envfile asOPENROUTER_API_KEY
pnpm devVisit http://localhost:3002 and enter your API token (desplega-dev-token-12345).
git init
git add .
git commit -m "Initial commit"
git remote add origin your-repo-url
git push -u origin main- Go to Vercel
- Import your GitHub repository
- Configure environment variables in Vercel dashboard:
DATABASE_URL- (will be auto-filled after creating Vercel Postgres)GMAIL_USER- Your Gmail addressGMAIL_APP_PASSWORD- Your Gmail app passwordOPENROUTER_API_KEY- Your OpenRouter API keyCRON_SECRET- A random secret string (e.g., useopenssl rand -hex 32)NEXT_PUBLIC_BASE_URL- Your Vercel deployment URL
- Deploy
- In your Vercel project, go to Storage tab
- Create a new Postgres database
- The
DATABASE_URLwill be automatically added to your environment variables - Run migrations from your local machine:
# Install Vercel CLI if you haven't
npm i -g vercel
# Pull environment variables
vercel env pull .env.production
# Run migrations
DATABASE_URL="<your-vercel-postgres-url>" pnpm prisma migrate deployOr use Vercel's built-in migration on deploy by adding to package.json:
"scripts": {
"postbuild": "prisma migrate deploy"
}After migrations, seed your production database:
# Using Vercel Postgres connection string
DATABASE_URL="<your-vercel-postgres-url>" pnpm prisma db seedOr create API keys manually using Prisma Studio:
DATABASE_URL="<your-vercel-postgres-url>" pnpm prisma studioThe cron jobs are pre-configured in vercel.json:
- Morning emails: 6:00 AM (UTC) -
0 6 * * * - Afternoon emails: 10:00 PM (UTC) -
0 22 * * * - Process email replies: Every 5 minutes -
*/5 * * * *
Note: Adjust the cron schedule in vercel.json if you need different timezones.
Make sure CRON_SECRET is set in your Vercel environment variables.
- Visit your Vercel URL
- Enter your API token
- Add founders in the configuration page
- Manually trigger cron jobs to test:
# Test morning email
curl -X GET https://your-app.vercel.app/api/cron/send-morning-emails \
-H "Authorization: Bearer your_cron_secret"
# Test email processing
curl -X GET https://your-app.vercel.app/api/cron/process-email-replies \
-H "Authorization: Bearer your_cron_secret"- Access Dashboard: Visit the app and enter your API token
- Configure Founders:
- Click "Configure Founders"
- Add founder name and email address
- Founders will automatically start receiving mood check emails
- Automated Emails: Founders receive emails at 6 AM and 10 PM asking about their mood
- Reply to Emails: Founders simply reply to the email with how they're feeling
- AI Processing: Every 5 minutes, the system:
- Checks for new email replies via Gmail IMAP
- Analyzes sentiment using GPT-4o-mini
- Categorizes mood on a 0-5 scale
- Stores the exact response text
- View Dashboard:
- Auto-refreshes every 30 seconds
- Click on any entry to see the exact email reply
- Filter by All, Weekly, or Monthly views
- See average mood and trends over time
Founders receive simple, plain-text emails:
Morning (6 AM):
Hi [Name],
How are you feeling today?
Just reply to this email with how you're doing.
Afternoon (10 PM):
Hi [Name],
How was your day?
Just reply to this email with how you're doing.
- Auto-refresh: Data updates every 30 seconds
- Clickable entries: Click any mood entry to see the exact email reply
- Period filters: View All, Weekly, or Monthly data
- Statistics: Total responses, average mood, pending responses
- Interactive chart: Hover over points to see details
- Mood colors: Visual color coding from red (terrible) to green (excellent)
- 0 - Terrible: Very negative, depressed, hopeless
- 1 - Bad: Negative, frustrated, struggling
- 2 - Meh: Neutral-negative, uninspired
- 3 - Okay: Neutral-positive, stable
- 4 - Good: Positive, motivated, making progress
- 5 - Excellent: Very positive, energized, thriving
POST /api/auth/validate- Validate API token
GET /api/founders- Get all founders (requires auth)POST /api/founders- Create a founder (requires auth)DELETE /api/founders/[id]- Delete a founder (requires auth)PATCH /api/founders/[id]- Update a founder (requires auth)
GET /api/mood?period=all|weekly|monthly- Get mood entries (requires auth)
GET /api/cron/send-morning-emails- Send morning emails to all foundersGET /api/cron/send-afternoon-emails- Send afternoon emails to all foundersGET /api/cron/process-email-replies- Poll Gmail IMAP and process replies
POST /api/test/process-mood- Manually process a mood entry (dev only)
GET /api/debug/check-emails- Check Gmail for unread emails (dev only)
id: Unique identifiertoken: API token for authenticationcompanyName: Company namefounders: Related founders
id: Unique identifiername: Founder nameemail: Founder email (unique)apiKeyId: Reference to API keymoodEntries: Related mood entries
id: Unique identifierfounderId: Reference to foundermood: Mood score (0-5)timeOfDay: "morning" or "afternoon"emailSentAt: When email was sentrespondedAt: When founder respondedrawResponse: Original email response text
- Check Gmail credentials in
.env:# Verify GMAIL_USER and GMAIL_APP_PASSWORD are set correctly - Ensure Gmail IMAP is enabled in Gmail settings
- Check if 2FA is enabled on your Google account
- Verify the app password is correct (16 characters, no spaces)
- Keep replies in INBOX: The system searches INBOX for emails from the last 24 hours. Since processing runs every 5 minutes, replies need to stay in INBOX for up to 5 minutes. After processing, you can archive them.
- If you archive immediately: Manually trigger processing right after replying:
curl -X GET http://localhost:3002/api/cron/process-email-replies \ -H "Authorization: Bearer your_random_secret_here" - Verify email subject contains
[MoodCheck-{entryId}]tag - Check dev server logs for IMAP connection errors
- Check that the founder email exists in the database
- Check if period filter is set correctly (use "All" to see everything)
- Verify API token is valid
- Check browser console for errors
- Ensure database has mood entries:
pnpm prisma studio
- Verify
vercel.jsonis in the project root - Check
CRON_SECRETis set in Vercel environment variables - View cron logs in Vercel dashboard
- Ensure your Vercel plan supports cron jobs
- Email Processing: Uses Gmail IMAP to poll INBOX for replies every 5 minutes (searches last 24 hours)
- Entry ID Tracking: Each email includes a unique entry ID in the subject for precise database matching
- Database as Source of Truth: Only processes emails with pending entries (
respondedAt: null) - AI Categorization: Uses GPT-4o-mini for fast, cost-effective sentiment analysis
- Database: Prisma generates client to
app/generated/prismato avoid conflicts - Mood Storage: Stores both the AI-categorized score (0-5) AND the exact email text
- Auto-refresh: Dashboard uses
setIntervalto fetch new data every 30 seconds
MIT