How to Build an AI Agent-Driven CRM in 30 Minutes: A Complete Local Deployment & Auto-Enrichment Guide
Learn how to deploy Comp AI CRM locally, configure OAuth authentication, and watch an autonomous AI agent scan emails, enrich contact/company info, and manage follow-ups without manual input.
How to Build an AI Agent-Driven CRM in 30 Minutes: A Complete Local Deployment & Auto-Enrichment Guide
If you work in sales or technical integration, you've likely faced this scenario: a new lead comes in, and you manually scour company websites, LinkedIn, and business registries to fill in the CRM fields line by line. A week later, you follow up only to find their title changed or the company rebranded—the CRM data is already stale. Even worse, most "AI CRMs" just slap a chatbot onto traditional forms. The dirty work of actually researching and entering data? Still on you.
In this tutorial, we'll dive straight into an open-source project that flips the script: Comp AI CRM (11.5K stars on GitHub). Its core philosophy is simple but powerful: The Agent isn't a feature inside the CRM; the CRM is where the Agent takes notes. Once deployed, the Agent works autonomously in the background at its own pace: scanning emails, researching contacts, enriching company profiles, and scheduling follow-ups. You can close the browser and it keeps running, writing verified data directly into the database while flagging uncertain info for your review.
Let's get it running locally step-by-step and walk through a complete "auto-enrichment" hands-on scenario.
Prerequisites
Before we start, make sure your machine has:
- Bun >= 1.0 (This project strictly uses Bun for dependency management and scripting;
nodewon't work) - Docker + Docker Compose (For running local Postgres)
- An email address on a domain you control (e.g.,
you@yourcompany.com), or a standard Gmail account
No Bun yet? Install it in one line:
bash
curl -fsSL https://bun.sh/install | bash
Also, note that this is a Turborepo monorepo containing three independent apps: apps/app (Next.js frontend, port 3000), apps/api (NestJS backend, port 3001), and apps/agent (the Agent process). They share a single .env file and Postgres database. Keeping this architecture in mind will make troubleshooting much easier later.
Step 1: Clone the Repo & Initialize
Open your terminal and run:
bash
git clone https://github.com/trycompai/crm.git && cd crm
cp .env.example .env
bun install
Pro Tip: By default, you'll clone the release branch (the latest tagged stable version), not main. This is great for production use to avoid bleeding-edge development bugs. If you plan to contribute code, remember to git switch main first.
bun install will install all workspace dependencies at once, which is significantly faster than running npm install in each subdirectory and ensures a consistent dependency tree.
Step 2: Start the Local Database
bash
docker compose up -d
This spins up a Postgres container locally on port 5432. The DATABASE_URL in your .env is pre-configured for postgresql://...:5432/crm, so no manual edits are needed here.
Why Docker instead of a system-installed Postgres? The project's Prisma migration scripts have strict Postgres version requirements. Docker guarantees a consistent environment for everyone. If your local Postgres version doesn't match, migrations will likely fail.
Step 3: Configure Environment Variables (Crucial)
Open .env. Here are the values you must configure:
env
BETTER_AUTH_SECRET=<generate using openssl>
ALLOWED_SIGN_IN=your-email-domain
GOOGLE_CLIENT_ID=<optional but highly recommended>
GOOGLE_CLIENT_SECRET=<optional but highly recommended>
Breakdown:
-
BETTER_AUTH_SECRET: The authentication signing key. Runopenssl rand -base64 32in your terminal to generate a secure string and paste it in. This secures your session cookies. Never use a placeholder in production. -
ALLOWED_SIGN_IN: This is the system's sole access control gate. If you set it toacme.com, any@acme.comemail can register and log in. Leave it blank, and no one can log in (this is an intentional security measure to prevent exposed deployments from being exploited). -
GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET: Google OAuth credentials. With these, you can not only log in via Google, but the CRM can also sync your Gmail and Calendar automatically. This gives the Agent the most reliable evidence source for tracking customer communication history (far more accurate than expensive third-party data, since no one can sell you an email a customer sent you).
Setting up Google OAuth is quick:
- Go to Google Cloud Console → OAuth client ID → Web application
- Set Authorized Redirect URI to:
http://localhost:3001/api/auth/callback/google - Enable Gmail API and Calendar API
- Paste the Client ID and Secret into
.env
You can run it without Google, but you'll miss the Google login button and email reading capabilities. At minimum, configure one OAuth provider (Google or Microsoft), otherwise authentication won't work.
Step 4: Run Migrations & Start the App
bash
bun run db:deploy # Apply Prisma migrations & create tables
bun run db:seed # Optional: seed demo data to see immediate results
bun run dev # Launch Frontend (3000), API (3001), and Agent
bun run dev will start all three services in the correct dependency order. You'll know it's successful when you see output like:
App: http://localhost:3000
API: http://localhost:3001
Open your browser to http://localhost:3000 and log in with an email matching your ALLOWED_SIGN_IN domain.
Hands-On: Let the Agent Auto-Enrich a Contact
After logging in, you'll notice the UI looks different from traditional CRMs. Every contact and company card has an "Agent" tab—this is the soul of the system.
1. Create a Contact Record
In the frontend, add a new contact (e.g., enter someone@techcorp.com). At this point, the record only contains that single email field.
2. Observe the Agent Tab
Switch to the contact's Agent tab. You'll see the Agent executing steps in real-time (output similar to below):
[agent] on Company brand data (Settings → General)
[agent] on LinkedIn (Settings → General)
[agent] off Web research (PERPLEXITY_API_KEY)
The Agent automatically calls built-in tools (18 in total), such as:
identify_contact– Reverse lookup identity from an email addressenrich_company– Fill in company details (Logo, Industry, Legal Name)read_crm_history– Scan your own email threads and signature blocks
Every action, evidence source, and confidence level is displayed transparently. The Agent operates under one iron rule: Never guess information about a person. Tools only report what they observe. Strong evidence is written directly to the record, while weak evidence becomes a suggestion pending your confirmation.
3. Check the Results
Return to the contact page after a few minutes. You'll find previously blank fields populated with: company logo, industry classification, the person's real name, and current title. If the Agent is unsure about a detail, it marks it as "Pending Confirmation" rather than guessing and potentially introducing errors.
This is the essence of "Agentic-first": The Agent isn't waiting for your prompts. It has its own task queue (lib/tasks.ts), processes tasks in parallel using FOR UPDATE SKIP LOCKED row-level locks, prioritizes autonomously, and schedules follow-ups on its own.
Troubleshooting & Common Pitfalls
1. DATABASE_URL Connection Failed on Startup
Verify the Postgres container is running:
bash
docker compose ps
If port 5432 is already in use, update the port in DATABASE_URL inside .env, or recreate the container with docker compose up -d --force-recreate.
2. No OAuth Buttons on Login Page
Check .env to ensure at least one pair of GOOGLE_CLIENT_ID/SECRET or MICROSOFT_CLIENT_ID/SECRET is configured. If neither is set, the login UI hides all buttons, preventing accidental open deployments.
3. Agent Shows All External Sources as "Off"
This doesn't mean the Agent is broken. Even without external API keys, it can still use read_crm_history to analyze your own emails and calendar—free and highly accurate evidence. Keys like PERPLEXITY_API_KEY are optional enhancers for web search.
4. What is bun run dev:session?
Since there's no traditional username/password login, all auth goes through OAuth. But during local development, you might not want to trigger the Google flow repeatedly. This command manually writes a session record and prints a cookie for debugging. It will refuse to run in production environments.
5. Production Deployment Notes
Deploy the three services (App, API, Agent) independently. The only mandatory shared values are DATABASE_URL and BETTER_AUTH_SECRET—if they mismatch, you'll fall into a redirect loop (API issues cookies, App can't verify them). Also, avoid regenerating apps/api/src/generated/server.ts in production builds; it's a committed artifact and regenerating it often fails due to missing GLIBC in most build images.
Summary
You've successfully:
- Set up the environment – Bun + Docker + Postgres pipeline
- Configured auth – Google/Microsoft OAuth setup & understood the
ALLOWED_SIGN_INphilosophy - Launched the full stack – Coordinated Next.js, NestJS API, and Agent services
- Ran an Agentic workflow – Watched the autonomous enrichment pipeline in action
The codebase itself is worth studying. Principles like "Keep intelligence out of the API layer", "UI strictly from packages/ui", and "No multi-tenant support" are battle-tested rules, not theoretical designs.
Next Steps:
- Dive into
apps/agentto study tool and skill files (likeskills/evidence.md) to understand Agent decision-making. - Try writing a custom Tool (just a TypeScript file).
- Configure
AGENT_BRIDGE_SECRETto chat directly with the Agent from the contact page.
Repo: trycompai/crm (MIT License). Grab it, deploy it, and see the difference.