HireMind.AI is a full-stack AI-powered mock interview platform. Users sign in with Google, configure role-based interviews (optionally from a resume), answer timed questions via voice or text, receive AI-evaluated feedback, and review detailed performance reports. A credits system gates interview sessions, with optional Razorpay payments to buy more credits.
- Tech Stack
- Architecture
- Project Structure
- Features & Implementation
- User Flow
- API Reference
- Data Models
- Credits System
- Environment Variables
- Getting Started
- Scripts
| Layer | Technologies |
|---|---|
| Frontend | React 19, Vite, Tailwind CSS 4, Redux Toolkit, React Router, Motion, Recharts, jsPDF |
| Backend | Node.js, Express 5, MongoDB (Mongoose) |
| Auth | Firebase Google Sign-In (client) + JWT HTTP-only cookies (server) |
| AI | OpenRouter API (openai/gpt-4o-mini) |
| Payments | Razorpay |
| Speech | Web Speech API (Speech Synthesis + WebKit Speech Recognition) |
┌─────────────────────────────────────────────────────────────────┐
│ React Client (Vite) │
│ Pages: Home, Auth, Interview, History, Pricing, Report │
│ State: Redux (userData) │ API: Axios (withCredentials) │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP + Cookies (JWT)
┌────────────────────────────▼────────────────────────────────────┐
│ Express Server (Node.js) │
│ Routes: /api/auth, /api/user, /api/interview, /api/payment │
│ Middleware: isAuth (JWT), Multer (PDF upload) │
└──────┬──────────────────┬──────────────────┬──────────────────────┘
│ │ │
▼ ▼ ▼
MongoDB OpenRouter Razorpay
The client talks to the backend over REST. Authentication uses a JWT stored in an HTTP-only cookie after Google sign-in. Protected routes verify the cookie via the isAuth middleware before running controller logic.
aiinterviews/
├── client/ # React frontend
│ ├── src/
│ │ ├── App.jsx # Routes + ServerUrl + session bootstrap
│ │ ├── main.jsx # React root, Redux Provider, Router
│ │ ├── pages/
│ │ │ ├── Home.jsx # Landing page
│ │ │ ├── Auth.jsx # Google sign-in
│ │ │ ├── InterviewPage.jsx # 3-step interview wizard
│ │ │ ├── InterviewHistory.jsx # Past interviews list
│ │ │ ├── InterviewReport.jsx # Report by interview ID
│ │ │ └── Pricing.jsx # Credit purchase plans
│ │ ├── components/
│ │ │ ├── Step1SetUp.jsx # Role, resume, question generation
│ │ │ ├── Step2Interview.jsx # Live interview (voice + timer)
│ │ │ ├── Step3Report.jsx # Analytics dashboard + PDF export
│ │ │ ├── Navbar.jsx # Credits, user menu, auth modal
│ │ │ ├── Timer.jsx # Countdown circular progress
│ │ │ ├── AuthModel.jsx # Modal wrapper for Auth
│ │ │ └── Footer.jsx
│ │ ├── redux/
│ │ │ ├── store.js
│ │ │ └── userSlice.js # Global user state
│ │ └── utils/
│ │ └── firebase.js # Firebase Auth config
│ └── index.html # Razorpay checkout script
│
└── server/ # Express backend
├── index.js # App entry, CORS, route mounting
├── config/
│ ├── connectDb.js # MongoDB connection
│ └── token.js # JWT generation
├── controllers/
│ ├── auth.controller.js
│ ├── user.controller.js
│ ├── interview.controller.js
│ └── payment.controller.js
├── middlewares/
│ ├── isAuth.js # JWT cookie verification
│ └── multer.js # PDF resume upload (5 MB)
├── models/
│ ├── user.model.js
│ ├── interview.model.js
│ └── payment.model.js
├── routes/
│ ├── auth.route.js
│ ├── user.route.js
│ ├── interview.route.js
│ └── payment.route.js
└── services/
├── openRouter.service.js # AI chat completions
└── razorPay.service.js # Razorpay client
What it does: Users sign in with their Google account. New users are created automatically; returning users are logged in.
How it works:
- Client (
client/src/pages/Auth.jsx): FirebasesignInWithPopupretrieves the user's name and email. - Client → Server:
POST /api/auth/googleauthsends{ name, email }withwithCredentials: true. - Server (
server/controllers/auth.controller.js):- Looks up user by email; creates one if missing.
- Signs a JWT (
userId, 7-day expiry) viagenToken. - Sets an HTTP-only
tokencookie (secure,sameSite: "none").
- Client: Dispatches
setUserDatato Redux with the returned user object. - Session restore (
client/src/App.jsx): On load,GET /api/user/current-userrehydrates the session from the cookie.
Logout: POST /api/auth/logout clears the cookie; Redux user state is set to null.
What it does: Users can upload a PDF resume. The app extracts text and uses AI to auto-fill role, experience, projects, and skills for personalized questions.
How it works:
- Client (
Step1SetUp.jsx): User selects a PDF;FormDatais sent toPOST /api/interview/resume. - Server (
interview.controller.js→analyzeResume):- Multer saves the file to
server/public/(5 MB limit). - pdfjs-dist parses each page and concatenates text.
- OpenRouter receives a system prompt asking for JSON:
{ role, experience, projects, skills }. - Parsed fields are returned; the uploaded file is deleted from disk.
- Multer saves the file to
- Client: Auto-populates the setup form and shows extracted projects/skills.
What it does: Generates exactly 5 interview questions tailored to role, experience, mode (HR/Technical), resume, projects, and skills.
How it works:
- Client (
Step1SetUp.jsx): On "Start Interview", sends{ role, experience, mode, resumeText, projects, skills }toPOST /api/interview/generate-questions. - Server (
generateQuestion):- Validates required fields and checks user has ≥ 50 credits.
- Builds a prompt from all candidate context.
- OpenRouter system prompt enforces:
- 5 questions, 15–25 words each, one per line
- Difficulty progression: easy → easy → medium → medium → hard
- Parses AI response into an array; assigns per-question
timeLimit(60s, 60s, 90s, 90s, 120s). - Deducts 50 credits and creates an
Interviewdocument in MongoDB.
- Client: Advances to Step 2 with
interviewId,questions,userName, and updatedcreditsLeft.
What it does: Simulates a real interview with an AI interviewer avatar, spoken questions, voice input, per-question timers, and instant feedback.
How it works (client/src/components/Step2Interview.jsx):
| Capability | Implementation |
|---|---|
| AI voice | window.speechSynthesis with a selected system voice (prefers Zira/Samantha for female, David/Mark for male) |
| Avatar video | Looped MP4 (male-ai.mp4 / female-ai.mp4) plays while AI speaks |
| Speech-to-text | webkitSpeechRecognition (continuous) appends transcripts to the answer textarea |
| Mic toggle | Start/stop recognition; mic is paused while AI speaks |
| Intro phase | Personalized greeting using userName, then first question |
| Timer | Timer.jsx shows circular countdown; auto-submits when time hits 0 |
| Submit answer | POST /api/interview/submit-answer with { interviewId, questionIndex, answer, timeTaken } |
| Feedback | Server returns AI feedback; client speaks it aloud before "Next Question" |
| Finish | After last question, POST /api/interview/finish aggregates scores and moves to Step 3 |
Answer evaluation (submitAnswer on server):
- Empty answer → score 0, feedback: "You did not submit an answer."
timeTaken > timeLimit→ score 0, feedback: "Time limit exceeded."- Otherwise, OpenRouter scores on confidence, communication, and correctness (0–10 each), computes
finalScoreas their average, and returns short human-like feedback (10–15 words).
What it does: Shows overall score, skill breakdown, per-question feedback, a performance trend chart, and downloadable PDF.
How it works:
- Immediate report (
Step3Report.jsx): Rendered afterfinishInterviewreturns aggregated data. - Historical report (
InterviewReport.jsx): FetchesGET /api/interview/report/:idfor past interviews from History. - Visualizations:
- Circular progressbar — overall score out of 10
- Skill bars — confidence, communication, correctness
- Recharts AreaChart — score trend across Q1–Q5
- Question breakdown — each question with score and AI feedback
- PDF export (
jsPDF+jspdf-autotable): GeneratesAI_Interview_Report.pdfwith score summary, advice, and a question table.
Server aggregation (finishInterview):
- Averages
score,confidence,communication,correctnessacross all questions. - Sets
interview.statusto"Completed"and storesfinalScore.
What it does: Lists all past interviews with role, experience, mode, date, score, and completion status.
How it works:
- Client (
InterviewHistory.jsx):GET /api/interview/get-interviewon mount. - Server (
getInterview): Returns interviews forreq.userId, sorted newest first, with selected fields. - Clicking a row navigates to
/report/:id.
What it does: Gates interview usage. New users start with 100 free credits; each interview costs 50 credits.
How it works:
- User model (
user.model.js):creditsdefaults to100. - Deduction:
generateQuestioncheckscredits >= 50, thenuser.credits -= 50. - Navbar: Displays current credit balance; popup links to
/pricingwhen more credits are needed. - Redux sync: Credit balance updates after question generation and successful payment.
What it does: Users can purchase credit packs via Razorpay.
Plans (Pricing.jsx):
| Plan | Price | Credits |
|---|---|---|
| Free (default) | ₹0 | 100 |
| Starter Pack | ₹100 | 150 |
| Pro Pack | ₹500 | 650 |
How it works:
- Create order:
POST /api/payment/order→ Razorpay order created;Paymentrecord saved with status"created". - Checkout: Razorpay popup opens (
window.Razorpayfromindex.htmlscript). - Verify: On success,
POST /api/payment/verifyvalidates HMAC signature (razorpay_order_id|razorpay_payment_idwithRAZORPAY_KEY_SECRET). - Credit grant: Payment marked
"paid"; user credits incremented via$inc.
What it does: Marketing homepage with feature highlights, interview modes, and CTAs.
How it works (Home.jsx):
- Animated sections (Motion) describing setup, voice interview, timers, and AI capabilities.
- "Start Interview" / "View History" require auth; unauthenticated users see
AuthModel. - Navbar (
Navbar.jsx): Brand, credits, user avatar menu (history, logout), auth modal trigger. - Footer: Product tagline.
What it does: Users choose Technical or HR interview style before starting.
How it works:
- Selected in
Step1SetUp.jsxdropdown; stored on theInterviewdocument (modeenum:"HR"|"Technical"). - Passed to the AI prompt so question tone and focus adapt (technical depth vs. behavioral/communication).
Sign in (Google)
│
▼
Home ──► Start Interview
│
▼
Step 1: Setup
• Enter role & experience (or auto-fill from resume)
• Optional: upload & analyze PDF resume
• Select HR or Technical mode
• Generate questions (costs 50 credits)
│
▼
Step 2: Live Interview
• AI intro + 5 timed questions
• Answer via voice and/or typing
• Submit → AI feedback per question
│
▼
Step 3: Report
• Scores, charts, question breakdown
• Download PDF
│
▼
History ──► View any past report
Pricing ──► Buy more credits
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /googleauth |
No | Google sign-in; sets JWT cookie |
| POST | /logout |
No | Clears JWT cookie |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /current-user |
Yes | Returns logged-in user |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /resume |
Yes | Upload PDF; AI extracts profile fields |
| POST | /generate-questions |
Yes | Generate 5 questions; deduct 50 credits |
| POST | /submit-answer |
Yes | Evaluate one answer with AI |
| POST | /finish |
Yes | Aggregate scores; mark completed |
| GET | /get-interview |
Yes | List user's interviews |
| GET | /report/:id |
Yes | Full report for one interview |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /order |
Yes | Create Razorpay order |
| POST | /verify |
Yes | Verify payment signature; add credits |
{
name: String, // required
email: String, // required, unique
credits: Number // default: 100
}{
userId: ObjectId,
role: String,
experience: String,
mode: "HR" | "Technical",
resumeText: String,
questions: [{
question: String,
difficulty: String, // easy | medium | hard
timeLimit: Number, // seconds
answer: String,
feedback: String,
score: Number, // 0–10
confidence: Number,
communication: Number,
correctness: Number
}],
finalScore: Number,
status: "Incomplete" | "Completed"
}{
userId: ObjectId,
planId: String,
amount: Number,
credits: Number,
razorpayOrderId: String,
razorpayPaymentId: String,
status: "created" | "paid" | "failed"
}| Action | Credit change |
|---|---|
| New user signup | +100 (default) |
| Start interview (generate questions) | −50 |
| Starter Pack purchase | +150 |
| Pro Pack purchase | +650 |
If credits fall below 50, generate-questions returns an error and the interview cannot start.
PORT=8000
MONGODB_URL=mongodb://localhost:27017/hiremind
JWT_SECRET=your_jwt_secret
OPENROUTER_API_KEY=your_openrouter_api_key
RAZORPAY_KEY_ID=your_razorpay_key_id
RAZORPAY_KEY_SECRET=your_razorpay_key_secretVITE_FIREBASE_APIKEY=your_firebase_api_key
VITE_RAZORPAY_KEY_ID=your_razorpay_key_idNote: Update
ServerUrlinclient/src/App.jsxto match your backend port (e.g.http://localhost:8000). The server default inindex.jsis6000ifPORTis unset — keep client and server ports aligned.
- Node.js 18+
- MongoDB (local or Atlas)
- Firebase project (Google Auth enabled)
- OpenRouter API key
- Razorpay account (for payments)
# Backend
cd server
npm install
# Frontend
cd ../client
npm installCreate server/.env and client/.env using the variables above.
Ensure Firebase Google sign-in is enabled and the Firebase config in client/src/utils/firebase.js matches your project.
Create the server/public/ folder for resume uploads (used by Multer).
# Terminal 1 — Backend
cd server
npm run dev
# Terminal 2 — Frontend
cd client
npm run devOpen http://localhost:5173 in a browser. Use Chrome for best Web Speech API support.
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server |
npm run build |
Production build |
npm run preview |
Preview production build |
npm run lint |
Run ESLint |
| Command | Description |
|---|---|
npm run dev |
Start server with nodemon |
| Path | Page | Description |
|---|---|---|
/ |
Home | Landing page |
/auth |
Auth | Standalone sign-in page |
/interview |
InterviewPage | 3-step interview flow |
/history |
InterviewHistory | Past interviews |
/pricing |
Pricing | Credit purchase plans |
/report/:id |
InterviewReport | Report for a specific interview |
- Cookie-based JWT instead of localStorage — tokens are HTTP-only for XSS resistance;
withCredentials: trueon all authenticated Axios calls. - OpenRouter as AI gateway — single
askAi()service wraps chat completions; used for resume parsing, question generation, and answer evaluation. - Per-question time limits — enforced client-side (timer UI) and server-side (
timeTaken > timeLimitcheck). - 3-step wizard —
InterviewPageholds local step state; report data flows from finish API or history fetch into the sameStep3Reportcomponent.
ISC (server package). See individual package.json files for dependency licenses.