AI-powered sales CRM built for modern teams. Unify leads, contacts, pipeline, tasks, and notes, then layer Gemini AI on top for summaries, risk scoring, email drafts, and sales insights.
A quick look at how Nevo CRM looks in action:
Landing Page — public marketing page with hero, features, testimonials, and pricing.
Dashboard — at-a-glance sales performance with stats, charts, and recent leads.
Pipeline — drag-and-drop Kanban board for managing deals across stages.
- What is Nevo CRM?
- Frontend Overview
- Frontend Architecture
- Frontend Pages & Features
- Backend API Reference
- Authentication & Security
- Data Models
- Getting Started
- Environment Variables
Nevo CRM is a full-stack, AI-powered SaaS-style sales customer relationship management platform. It helps sales teams and founders manage their entire sales workflow in one place:
- Leads & Contacts — track prospects and customers in a unified database
- Visual Pipeline — drag-and-drop Kanban board to move deals through stages
- Tasks & Follow-ups — never miss a follow-up with smart task management
- Notes — pin important context to leads and contacts
- AI Co-Pilot — Gemini-powered lead summaries, risk scoring, email drafts, and pipeline health insights
- Live Analytics — real-time dashboards showing conversion rates, revenue, and source breakdowns
The app uses React 19 + Vite on the frontend and Express 5 + MongoDB on the backend, with JWT authentication and Google Gemini AI integration.
The frontend is a modern single-page application (SPA) built with React 19, bundled with Vite, and styled with Tailwind CSS v4.
The frontend serves as the primary workspace for sales professionals. It provides:
- A public landing page (
/) that markets the product, showcases features, displays testimonials, and presents pricing tiers - Authenticated app routes (
/app/*) that serve as the actual CRM workspace after login - A responsive, mobile-friendly design with a violet/purple brand theme
- Real-time interactivity including drag-and-drop pipeline management, live charts, and AI-powered dialogs
The product is branded as Nevo CRM with a distinctive visual identity:
- Color palette: Violet/purple gradients (
#7c3aedto#c026d3to#f43f5e) - Background: Soft lavender (
#f8f7ff) - Typography: Bold display headings with clean sans-serif body text
- Icons: Lucide React icon library
- Mascot/Logo: Custom
NevoLogocomponent
Note on images: This documentation cannot embed actual screenshots because the environment does not support image input. The descriptions below are based on the actual React component code.
The landing page is a full marketing experience containing:
Hero Section
- Gradient badge: "Powered by Gemini AI"
- Main headline: "The CRM that works as fast as you do."
- Subheadline explaining the unified lead/contact/pipeline + AI value proposition
- Two CTAs: "Start for free" (gradient button) and "Sign in to your workspace"
- Interactive pipeline mockup: A scaled, non-interactive preview of the Kanban board showing demo deal cards across New, Qualified, Proposal, Won, and Lost stages. The mockup includes stat tiles (Total pipeline: $24,500, Open deals: 8, Won value: $9,200, Win rate: 34%) and a floating animation effect.
Features Section
- Six feature cards in a responsive grid:
- Visual Pipeline — Drag-and-drop Kanban board
- AI Co-Pilot — Gemini-powered lead scoring and email drafts
- Lead & Contact Hub — Unified database
- Follow-up Reminders — Smart task lists
- Live Analytics — Real-time dashboards
- Secure & Private — JWT auth and encrypted credentials
Testimonials Section
- Three customer quotes from sales directors, founders, and heads of growth with 5-star ratings
Pricing Section
- Three tiers: Starter (Free), Pro ($29/mo), Enterprise (Custom)
- The Pro plan is highlighted with a gradient background and "Most popular" badge
CTA Banner
- Full-width gradient banner with the message "Ready to supercharge your sales?"
- "Get started free" button
Footer
- Logo, copyright, and navigation links
Once logged in, users enter the authenticated app shell consisting of:
Layout Components
AppLayout— Main wrapper with sidebar and content areaSidebar/IconRail— Navigation rail with icons for Dashboard, Leads, Contacts, Pipeline, Tasks, Notes, and SettingsTopbar/TopNav— Header with user info and logoutProtectedRoute— Route guard that redirects unauthenticated users to/login
| Technology | Purpose |
|---|---|
| React 19.2 | UI framework |
| Vite 8 | Build tool and dev server |
| React Router 7 | Client-side routing |
| Tailwind CSS 4 | Utility-first styling |
| Axios | HTTP client with interceptors |
| React Hook Form | Form state management |
| Recharts 3 | Dashboard charts (bar, area, pie/donut) |
| @dnd-kit | Drag-and-drop Kanban pipeline |
| Lucide React | Icon library |
| Sonner | Toast notifications |
| date-fns | Date formatting |
| clsx + tailwind-merge | Conditional class merging |
frontend/
├── src/
│ ├── main.jsx # React entry point
│ ├── App.jsx # Central route table
│ ├── App.css / index.css # Global styles
│ ├── context/
│ │ └── AuthContext.jsx # Authentication state management
│ ├── lib/
│ │ ├── api.js # Axios instance with JWT interceptor
│ │ ├── services.js # API service functions
│ │ ├── constants.js # App constants
│ │ └── format.js # Date/number formatters
│ ├── components/
│ │ ├── layout/ # AppLayout, Sidebar, Topbar, ProtectedRoute
│ │ ├── ai/ # AiEmailDialog, AiInsightsCard
│ │ ├── leads/ # LeadDrawer, LeadFormDialog
│ │ ├── dashboard/ # HeroCard
│ │ ├── common/ # ConfirmDialog, EmptyState, NevoLogo, PageHeader, StatCard
│ │ └── ui/ # Reusable UI primitives (Avatar, Badge, Button, etc.)
│ └── pages/
│ ├── Landing.jsx # Public marketing page
│ ├── Dashboard.jsx # Overview with charts and stats
│ ├── Leads.jsx # Lead management list
│ ├── Contacts.jsx # Contact management
│ ├── Pipeline.jsx # Kanban drag-and-drop board
│ ├── Notes.jsx # Notes tied to leads/contacts
│ ├── Tasks.jsx # Task and follow-up management
│ ├── Settings.jsx # User profile settings
│ └── auth/
│ ├── Login.jsx # Login form
│ └── Register.jsx # Registration form
- AuthContext: Provides
user,login,register,logout, andloadingstate across the app - Local component state: Used for forms, modals, and UI toggles
- No global state library: The app relies on React context and local state
The api.js file creates an Axios instance with:
- Base URL from
VITE_API_URL(defaults tohttp://localhost:8000/api) - JWT token interceptor that adds
Authorization: Bearer <token>to every request - Auto-logout on 401 Unauthorized responses
- Request/response logging in development
Purpose: Convert visitors into registered users.
Key elements:
- Sticky navbar with logo, feature links, sign-in link, and "Get started free" CTA
- Hero section with animated gradient text and floating pipeline mockup
- Feature grid with hover animations
- Customer testimonials with star ratings
- Three-tier pricing cards
- Final CTA banner with gradient background
- Footer with navigation
Purpose: Authenticate existing users.
Features:
- Email and password form
- Validation and error display
- Redirect to
/appon success - Link to registration page
Purpose: Onboard new users.
Features:
- Name, email, company, password, and confirm password fields
- Password validation (minimum length)
- Automatic login after registration
- Redirect to
/appon success
Purpose: Provide an at-a-glance view of sales performance.
Features:
- Stat cards showing total leads, contacts, tasks, and revenue
- Recharts visualizations:
- Engagement bar chart — leads by engagement level
- Revenue area chart — revenue trends over time
- Leads by source donut chart — breakdown of lead origins
- Pipeline by stage bars — deal distribution across pipeline stages
- Recent leads table
- Quick action buttons
Purpose: Manage the lead database.
Features:
- Searchable, filterable lead list (by status, priority, source)
- Create, edit, and delete leads
- Lead drawer with full details
- Status badges (New, Qualified, Proposal, Won, Lost)
- Priority indicators (Low, Medium, High)
- Source tracking (Website, Referral, Cold Outreach, Social, Event, Other)
- AI summary and risk score display
Purpose: Manage contact relationships.
Features:
- Searchable contact list (by name, email, company)
- Tag-based filtering
- Create, edit, and delete contacts
- Contact details: name, email, phone, company, title, notes, tags
- Favorite/star contacts
- Text search across name, email, and company fields
Purpose: Visual deal management with drag-and-drop.
Features:
- Kanban board with columns: New, Qualified, Proposal, Won, Lost
- Drag-and-drop using
@dnd-kitto move leads between stages - Column totals showing pipeline value per stage
- Deal cards with avatar initials, company, value, and priority badge
- "AI suggest next step" button on each card
- Visual feedback during drag operations
Purpose: Capture and organize context around leads and contacts.
Features:
- Create notes linked to specific leads or contacts
- Search notes by content
- Pin important notes to the top
- Edit and delete notes
- Chronological list view
Purpose: Track follow-ups and action items.
Features:
- Task list with status (Pending, In Progress, Completed)
- Priority levels (Low, Medium, High)
- Due dates with date picker
- Link tasks to leads or contacts
- Auto-set
completedAttimestamp when marking complete - Filter by status, priority, or related lead
Purpose: Manage user profile and preferences.
Features:
- Update display name and company
- Change avatar
- Update password
- View current subscription tier
- Account information display
Base URL: http://localhost:8000/api (configurable via VITE_API_URL)
All endpoints except /api/auth/register and /api/auth/login require a valid JWT token in the Authorization: Bearer <token> header.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/health |
No | Returns server status |
Response
{
"success": true,
"message": "Server is running!"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
No | Create a new user account |
| POST | /api/auth/login |
No | Authenticate and receive JWT |
| GET | /api/auth/me |
Yes | Get current user profile |
| PUT | /api/auth/profile |
Yes | Update user profile |
POST /api/auth/register
- Body:
{ name, email, password, company? } - Response:
{ success, token, user: { id, name, email, company, role } }
POST /api/auth/login
- Body:
{ email, password } - Response:
{ success, token, user: { id, name, email, company, role } }
GET /api/auth/me
- Response:
{ success, user: { id, name, email, company, role, avatar } }
PUT /api/auth/profile
- Body:
{ name?, company?, avatar?, password? } - Response:
{ success, user }
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/leads |
Yes | List all leads (with filters) |
| POST | /api/leads |
Yes | Create a new lead |
| GET | /api/leads/:id |
Yes | Get a single lead by ID |
| PUT | /api/leads/:id |
Yes | Update a lead |
| DELETE | /api/leads/:id |
Yes | Delete a lead |
| PATCH | /api/leads/reorder |
Yes | Bulk reorder pipeline stages |
GET /api/leads — Query Parameters
status— Filter by lead status (New, Qualified, Proposal, Won, Lost)priority— Filter by priority (Low, Medium, High)source— Filter by source (Website, Referral, Cold Outreach, Social, Event, Other)search— Search across name, email, company, and notes
PATCH /api/leads/reorder — Body
{
"updates": [
{ "id": "lead_id_1", "status": "Qualified", "order": 1 },
{ "id": "lead_id_2", "status": "New", "order": 0 }
]
}Lead Object
{
"id": "string",
"name": "string",
"email": "string",
"phone": "string",
"company": "string",
"status": "New | Qualified | Proposal | Won | Lost",
"priority": "Low | Medium | High",
"source": "Website | Referral | Cold Outreach | Social | Event | Other",
"value": "number",
"notes": "string",
"tags": ["string"],
"aiSummary": "string",
"aiRiskScore": "number",
"owner": "user_id",
"createdAt": "ISO date",
"updatedAt": "ISO date"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/contacts |
Yes | List all contacts (with filters) |
| POST | /api/contacts |
Yes | Create a new contact |
| GET | /api/contacts/:id |
Yes | Get a single contact by ID |
| PUT | /api/contacts/:id |
Yes | Update a contact |
| DELETE | /api/contacts/:id |
Yes | Delete a contact |
GET /api/contacts — Query Parameters
search— Search across name, email, and companytag— Filter by specific tag
Contact Object
{
"id": "string",
"name": "string",
"email": "string",
"phone": "string",
"company": "string",
"title": "string",
"tags": ["string"],
"notes": "string",
"favorite": "boolean",
"owner": "user_id",
"createdAt": "ISO date",
"updatedAt": "ISO date"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/tasks |
Yes | List all tasks (with filters) |
| POST | /api/tasks |
Yes | Create a new task |
| PUT | /api/tasks/:id |
Yes | Update a task |
| DELETE | /api/tasks/:id |
Yes | Delete a task |
GET /api/tasks — Query Parameters
status— Filter by status (Pending, In Progress, Completed)priority— Filter by priority (Low, Medium, High)relatedLead— Filter by associated lead ID
Task Object
{
"id": "string",
"title": "string",
"description": "string",
"dueDate": "ISO date",
"status": "Pending | In Progress | Completed",
"priority": "Low | Medium | High",
"relatedLead": "lead_id",
"relatedContact": "contact_id",
"completedAt": "ISO date",
"owner": "user_id",
"createdAt": "ISO date",
"updatedAt": "ISO date"
}Note: When a task is marked as Completed, the backend automatically sets the completedAt timestamp.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/notes |
Yes | List all notes (with filters) |
| POST | /api/notes |
Yes | Create a new note |
| PUT | /api/notes/:id |
Yes | Update a note |
| DELETE | /api/notes/:id |
Yes | Delete a note |
GET /api/notes — Query Parameters
lead— Filter by lead IDcontact— Filter by contact IDsearch— Search note content
Note Object
{
"id": "string",
"content": "string",
"lead": "lead_id",
"contact": "contact_id",
"pinned": "boolean",
"owner": "user_id",
"createdAt": "ISO date",
"updatedAt": "ISO date"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/ai/status |
Yes | Check if AI is configured |
| POST | /api/ai/lead-summary |
Yes | Generate AI summary for a lead |
| POST | /api/ai/generate-email |
Yes | Generate an AI email draft |
| POST | /api/ai/sales-insights |
Yes | Get AI sales insights |
GET /api/ai/status
{
"configured": true,
"model": "gemini-3.5-flash"
}POST /api/ai/lead-summary — Body
{
"lead": {
/* full lead object */
}
}OR
{
"leadId": "lead_id"
}Response
{
"summary": "AI-generated summary of the lead...",
"riskScore": 75,
"suggestedPriority": "High"
}POST /api/ai/generate-email — Body
{
"lead": {
/* full lead object */
},
"purpose": "follow-up",
"tone": "professional"
}OR
{
"leadId": "lead_id",
"purpose": "proposal",
"tone": "friendly"
}Response
{
"subject": "Following up on our conversation",
"body": "Hi [Name], ..."
}POST /api/ai/sales-insights — Body
{
"stats": {
/* optional sales statistics */
}
}Response
{
"headline": "Pipeline health is improving",
"insights": ["Insight 1", "Insight 2"],
"recommendations": ["Action 1", "Action 2"],
"healthScore": 82
}Note: If
statsis not provided, the backend computes statistics from the authenticated user's leads.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/analytics/overview |
Yes | Get dashboard analytics data |
Response
{
"stats": {
"totalLeads": 150,
"totalContacts": 80,
"totalTasks": 45,
"completedTasks": 32,
"totalRevenue": 125000
},
"pipeline": [
{ "stage": "New", "count": 20, "value": 15000 },
{ "stage": "Qualified", "count": 15, "value": 25000 },
{ "stage": "Proposal", "count": 10, "value": 40000 },
{ "stage": "Won", "count": 8, "value": 35000 },
{ "stage": "Lost", "count": 5, "value": 10000 }
],
"trend": [
{ "date": "2024-01", "leads": 12, "revenue": 8000 },
{ "date": "2024-02", "leads": 15, "revenue": 12000 }
],
"recentLeads": [
{ "id": "string", "name": "string", "status": "string", "value": "number" }
]
}All protected endpoints require a Bearer token in the request header:
Authorization: Bearer <your_jwt_token>
- Tokens expire after 7 days (configurable via
JWT_EXPIRES_IN) - On 401 Unauthorized, the frontend automatically logs the user out
- Tokens are stored in the browser's local storage via the AuthContext
- Passwords are hashed using bcrypt before storage
- Passwords are excluded from JSON responses (
select: falsein Mongoose schema) - Minimum password length: 6 characters
The backend allows requests only from the configured CLIENT_URL (default: http://localhost:5173).
| Field | Type | Description |
|---|---|---|
name |
String | Full name (required) |
email |
String | Unique email address (required, lowercase) |
password |
String | Bcrypt-hashed password (required, min 6 chars) |
role |
Enum | owner or member (default: owner) |
company |
String | Company name |
avatar |
String | Avatar image URL |
| Field | Type | Description |
|---|---|---|
owner |
ObjectId | Reference to User (required) |
name |
String | Lead name (required) |
email |
String | Email address |
phone |
String | Phone number |
company |
String | Company name |
status |
Enum | New, Qualified, Proposal, Won, Lost (default: New) |
priority |
Enum | Low, Medium, High |
source |
Enum | Website, Referral, Cold Outreach, Social, Event, Other |
value |
Number | Deal value in currency |
notes |
String | Lead notes |
tags |
[String] | Array of tags |
aiSummary |
String | AI-generated summary |
aiRiskScore |
Number | AI-calculated risk score (0-100) |
| Field | Type | Description |
|---|---|---|
owner |
ObjectId | Reference to User (required) |
name |
String | Contact name (required) |
email |
String | Email address |
phone |
String | Phone number |
company |
String | Company name |
title |
String | Job title |
tags |
[String] | Array of tags |
notes |
String | Contact notes |
favorite |
Boolean | Starred/favorite contact |
| Field | Type | Description |
|---|---|---|
owner |
ObjectId | Reference to User (required) |
title |
String | Task title (required) |
description |
String | Task description |
dueDate |
Date | Due date |
status |
Enum | Pending, In Progress, Completed (default: Pending) |
priority |
Enum | Low, Medium, High |
relatedLead |
ObjectId | Reference to Lead |
relatedContact |
ObjectId | Reference to Contact |
completedAt |
Date | Auto-set when status becomes Completed |
| Field | Type | Description |
|---|---|---|
owner |
ObjectId | Reference to User (required) |
content |
String | Note content (required) |
lead |
ObjectId | Reference to Lead (optional) |
contact |
ObjectId | Reference to Contact (optional) |
pinned |
Boolean | Pin note to top of list |
- Node.js 18+ and npm
- MongoDB Atlas account (or local MongoDB)
- Google Gemini API key
cd backend
npm installCreate a .env file in the backend/ directory:
PORT=8000
NODE_ENV=development
CLIENT_URL=http://localhost:5173
MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
GEMINI_API_KEY=your_gemini_api_key
GEMINI_MODEL=gemini-3.5-flashStart the backend server:
npm run devThe API will be available at http://localhost:8000.
cd frontend
npm installCreate a .env file in the frontend/ directory (optional):
VITE_API_URL=http://localhost:8000/apiStart the frontend dev server:
npm run devThe app will be available at http://localhost:5173.
cd frontend
npm run buildThe production build will be output to frontend/dist/.
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 8000 |
NODE_ENV |
Environment mode | development |
CLIENT_URL |
Allowed CORS origin | http://localhost:5173 |
MONGODB_URI |
MongoDB connection string | — |
JWT_SECRET |
Secret key for JWT signing | — |
JWT_EXPIRES_IN |
Token expiration time | 7d |
GEMINI_API_KEY |
Google Gemini API key | — |
GEMINI_MODEL |
Gemini model name | gemini-3.5-flash |
| Variable | Description | Default |
|---|---|---|
VITE_API_URL |
Backend API base URL | http://localhost:8000/api |


