Build Your Own CRM with Claude Code

Emma Guetta

Emma Guetta, co-founder of Retriever. I use these systems every day to sell it.

Before you dive in, a word about Retriever, the product this guide comes from. It's a go-to-market AI agent that builds qualified prospect lists from one prompt: you describe your ICP in plain English, the agent searches across 20+ data sources, qualifies every company against your criteria, and returns a ranked list with its reasoning visible. The guides in this section document the systems I use to sell it.

The guide

A guide by Emma Guetta, founder of Retriever


Who this is for

Solopreneurs and small async teams running B2B sales (outbound, inbound, or both), who already know Claude Code a little and want a CRM that bends to them, not the other way around.

Not for you if:

  • You're a sales team of 20 working on the same pipeline live. No concurrent-write protection, you'll collide.
  • You sell self-serve SaaS with no human sales motion. You don't need a CRM, you need product analytics.
  • You've never opened Claude Code. It's a prerequisite, not optional.

Heads up before you start

I built this CRM for myself. I use it alone. A few things to know before you fork or rebuild it:

  • It's not deployed. The app runs locally on your machine at http://localhost:5174. There's no hosted version, no login, no shared database.
  • The "database" is plain text on disk. Three TSV files in data/, edited atomically by a Vite middleware. No Postgres, no Supabase, no cloud anything.
  • Single-user by design. Two people editing at the same time on the same machine will collide, and the audit trail won't tell you who's who.
  • For team use: sync via git. Each person clones the repo, edits locally, commits, pushes. Conflicts on the TSVs are resolved like any code merge — they show up as standard git diffs.
  • No concurrent-write protection. A single user can't corrupt their TSV (atomic writes), but if two processes write simultaneously, the last one wins.

In short: built for solopreneurs and small async teams. Not for 20 sales reps hammering on the same row at the same time.


Why I built my own CRM

Two things converged.

First, I had moved most of my work into Claude Code. Strategy docs, product notes, content drafts, all in one folder I opened every morning.

Second, the CRMs I had tried did not quite fit. Some fields I needed weren't there, some integrations sat behind a higher tier, some automations did 30% of what I actually wanted.

So I built a CRM inside the same folder. It took an afternoon. A few weeks later, with skills layered on top, it had become the one I actually wanted to use.

Here is what I get out of it now:

Logging calls by voice

I dictate what I need to log. Wispr Flow transcribes, Claude scores the prospect, fills the right columns, drafts the next action, and waits for my "yes" before writing.

"I just talked to Sarah at Acme. Head of Sales, team of 25, already using Apollo. Wants a demo Wednesday."

Importing 100 leads from a CSV in 30 seconds

I paste a CSV and Claude detects the format, creates companies and contacts in bulk, scores them, asks me about anything ambiguous.

Asking real questions about my pipeline

Because everything is plain text, Claude can read it all and answer.

"Which company types convert best from Disco to Demo?"

"Who in my Hot pipeline hasn't been contacted in 3 weeks?"

No dashboard to build hy hand. No SQL to write.

Drafting follow-ups in my voice

Claude has the full context (the call summary, the prospect's role, the company size). I ask for a DM, it writes one that actually fits.

Letting Claude take initiative

When Claude notices in our conversation that a piece of information should land in the CRM, it asks me first and then logs it on its own.


What I'm sharing

The repo is here: https://github.com/emmaguetta/open-source-crm

You can grab it with:

git clone https://github.com/emmaguetta/open-source-crm.git
cd open-source-crm/crm-app
npm install
npm run dev

Open http://localhost:5174. You should see an empty dashboard. That is the whole install.

What's in the repo:

  • A small Vite + React app that reads and writes 3 TSV files (companies, contacts, calls).
  • Two markdown brain files (docs/icp.md, docs/crm-rules.md) shipped as empty templates.
  • A generic schema. No business-specific columns. No data of mine.

What's not in the repo:

  • My data (obviously).
  • The skills I wrote on top. Those are specific to my workflow, and you will write better ones for yours.

Now build yours

You have two paths from here. Pick the one that fits how you like to work.

What every CRM needs, regardless of which path you take

Before you start, get clear on what a CRM actually is, stripped of vendor marketing:

  1. A list of companies you care about (prospects, clients, partners, investors, anyone you interact with on the business side).
  2. A list of people inside those companies linked to their company.
  3. A log of every interaction (calls, DMs, emails, meetings) linked to the person.
  4. A way to score and prioritize so you know who to talk to next.
  5. A way to filter and slice so you can pull a Hot list, a stalled list, a "haven't talked to in 3 weeks" list.

That is it. Everything else (automation, integrations, dashboards) is layered on top of these five things. My CRM ships these five things and nothing more — the rest is for you to add as you need it.

What you get out of the box, concretely

A quick visual before the column lists — here's what the app looks like once you run it locally.

Dashboard — counts, scoring breakdowns, top opportunities and cold-but-promising contacts surfaced at a glance.

Dashboard view

Contacts — sortable table with saved views (Default, Hot prospects, Go-to-market, ICP 3, Lead Magnet, Founders, etc. — you create your own), filters across every column, and color-coded tags for category, relationship, temperature, ICP score.

Contacts view

Companies — same idea, focused on company-level data: domain, industry, tagline, ICP type, relationship, funnel stage, ICP score.

Companies view

Now the exact columns the repo ships with. These are the generic columns I kept — everything specific to my Retriever business (ICP scores, persona types, lead magnet tracking, funding rounds) was stripped. You'll add the columns that matter to you in the next steps.

data/companies.tsv — one row per company id, name, website, domain, linkedin_url, industry, tagline, funnel_stage, employees, revenue, currency, city, region, country, summary, notes, next_action, created_by, created_at, updated_by, updated_at

data/contacts.tsv — one row per person, linked to a company via company_id id, company_id, name, job_title, linkedin, email, phone, city, region, country, funnel_stage, temperature, recap, next_action, created_at, source, created_by, updated_by, updated_at

data/calls.tsv — one row per interaction, linked to a contact via contact_id id, contact_id, date, type, summary, next_action, created_by, created_at, updated_by, updated_at

The repo also ships two empty markdown files in docs/: icp.md and crm-rules.md. They are templates. They contain section headers and placeholder instructions, no real content. You have to fill them in. That's not optional. The whole system depends on those two files because Claude Code reads them before doing anything in the CRM.

What's in icp.md, and what you need to write

docs/icp.md is your scoring model. Claude reads it every time you add or update a row, so it knows how to score prospects against your criteria.

A good icp.md answers these four questions:

  1. What types of companies do I sell to? Mine has 7 (solopreneur, small agency, established startup, etc.). Yours might have 3. For each type, write a one-line description of what makes a company belong to that bucket.
  2. For each type, what 3 criteria validate the perfect fit? Each criterion = 1 point. A company validating all 3 gets a score of 3/3. Be concrete: "has a defined ICP", "team of 20+ in sales", "spends >$500/month on outreach tools". Not "is a good fit".
  3. Which roles inside those companies do I target, and what score do they get? Map each role to a 0-3 score. Founders might be 3, mid-management might be 2, anyone else 0.
  4. What's the one signal that matters more than all the others? The thing that, if missing, almost disqualifies a lead. For me, it's "having a clearly defined ICP". For you, it might be "team size", "tech sophistication", "intent signal".

Write what you actually use when you qualify a prospect today. If you have to invent something for the doc, you're overengineering it.

What's in crm-rules.md, and what you need to write

docs/crm-rules.md is your schema and conventions. Claude reads it before adding columns, validating entries, or cleaning duplicates.

A good crm-rules.md answers these four questions:

  1. What does each column mean? Don't just list "funnel_stage". Say: "funnel_stage = where this prospect is in my sales process. Values: Lead -> Disco -> Demo -> Nego -> Win/Lost." Cover every column you keep or add.
  2. Which columns have a fixed list of values? Document the dropdowns. (e.g., temperature is Hot/Warm/Cold; source is Outbound LinkedIn / Inbound / Warm Intro / Lead Magnet / Other.)
  3. What format conventions apply? Dates in YYYY-MM-DD. IDs in lowercase-kebab-case. No commas inside non-list columns. Phone numbers in free text.
  4. What edge cases come up in my business? Examples from mine: "Students aren't companies, put the school name in job_title and leave company_id empty"; "VCs aren't in the sales funnel, leave funnel_stage empty"; "A contact can have multiple relationship_type tags."

Write these down as you encounter them. The first version is rough, the third version is solid.

Time budget for the brain files

Plan 30 minutes total. About 20 on icp.md (the scoring criteria are where the value sits), 10 on crm-rules.md (faster once you know your columns).

You can iterate after launch — refine the rules whenever Claude scores something wrong.

Path A — Fork mine and adapt it

Best if you want to move fast and prefer to delete rather than design.

  1. Fork the repo on GitHub.
  2. Clone your fork locally and open the folder in Claude Code.
  3. Paste the prompt below.
I just cloned this CRM repo and want to adapt it to my business.

First, read these files to understand what you're working with:
- README.md
- docs/icp.md and docs/crm-rules.md (the brain files, shipped empty)
- data/companies.tsv, data/contacts.tsv, data/calls.tsv (header rows only)
- crm-app/src/lib/properties.ts and crm-app/src/lib/fieldOptions.ts (the schema and enums)

Then ask me these questions one at a time:
1. What is my business? What do I sell, and to whom?
2. How do I find prospects? (outbound LinkedIn / inbound / lead magnet / referrals)
3. What columns should I add, remove, or rename on companies, contacts, and calls? Remind me the existing columns
4. How do I want to score prospects? (a 0-3 ICP score? a tag system? a custom scale?)
5. What is the one signal that matters more than the others?

Once I've answered:
- Update the TSV headers in data/ to match my column choices.
- Fill in docs/icp.md with my scoring model.
- Fill in docs/crm-rules.md with my schema and conventions.
- Update crm-app/src/lib/properties.ts, fieldOptions.ts, and parseTSV.ts so
  the app reflects the new schema (types, dropdowns, columns shown in views).
- Show me a full list of everything you plan to change before writing.

Wait for my "yes" before writing anything.

That single prompt drives the entire adaptation: discovery, brain files, TSVs, app code, all in one consistent pass. Stop and refine whenever Claude asks.

Path B — Build from scratch

Best if your business is very different from mine, or if you want full control over the structure.

Open Claude Code in an empty folder, then paste this prompt:

I want to build my own CRM from scratch in this folder. Plain text, git-friendly,
controlled via Claude Code.

Here's the architecture I want:

1. THREE TSV TABLES in data/ — this is my "database"
   - companies.tsv: one row per company (prospects, clients, partners, investors)
   - contacts.tsv: one row per person, with a company_id foreign key
   - calls.tsv: one row per interaction (call, DM, email, meeting), with a contact_id
     foreign key
   - Header row first line. Tab-separated. UTF-8. One trailing newline.
   - Two sidecar JSON files: crm-audit.json (per-cell who-edited-what) and
     crm-views.json (saved filter/sort/columns configs).

2. TWO BRAIN FILES in docs/, written in plain English
   - icp.md: my scoring model (the types of companies I sell to, the criteria a
     prospect needs to validate to score 1/2/3 out of 3, the personas I target,
     the one signal that matters most)
   - crm-rules.md: my schema and conventions (column list per table, dropdown
     values, format conventions like ISO dates and kebab-case slugs, edge cases)

3. A SMALL LOCAL WEB APP in crm-app/
   - Reads the 3 TSVs from disk and serves them at http://localhost:5174
   - A dashboard view with counters by funnel stage and temperature
   - A companies view, a contacts view, a calls view (sortable, filterable tables)
   - Detail views (click a row to see related entities)
   - Inline editing (click a cell, edit, the change writes back to the TSV)
   - Saved views and audit trail (sidecar JSONs above)

CODE GUIDANCE — stack and conventions I want you to follow:

- Vite + React 19 + TypeScript. No Next.js, no Remix, no SSR — this app is
  single-user and local only.
- Tailwind CSS v4 for styling. No external UI kit. Components built from scratch
  with Tailwind utilities; Headless UI patterns are fine.
- TanStack Table v8 for the data tables (sorting, filtering, column config).
- DnD Kit for column reordering and drag interactions.
- No external database. The 3 TSVs ARE the database. Read them on app load,
  re-fetch after any write.
- No HTTP backend, no API server. Use a Vite plugin (`configureServer`) to
  expose internal endpoints (GET/POST /api/companies, /api/contacts, /api/calls,
  /api/audit, /api/views) directly inside the dev server. The middleware reads
  and writes the files on the local filesystem.
- Writes must be ATOMIC: write to a `.tmp` file, then rename to the target
  filename. This is the only way a single-user CRM stays uncorrupted if the
  process crashes mid-write.
- Every write to a TSV cell also shifts an entry in crm-audit.json
  (current -> previous, with `by` and `at` ISO date).
- TSV parser reads headers dynamically — no hardcoded column lists in the parser
  itself. Add a single `properties.ts` config file that lists each column with
  its type, source, description, and enum values. This is the schema-of-record.
- TypeScript types stay loose on the parsed rows (Record<string, string>) and
  strict on the property metadata. The schema lives in data and config, not in
  the type system.
- No authentication. No user accounts. The audit trail uses a free-text "me"
  field, no login.

Before writing anything, ask me these questions one at a time:
1. What is my business? What do I sell, and to whom?
2. How do I find prospects? (outbound / inbound / lead magnet / referrals)
3. What is my volume? (a few prospects a week / hundreds per month)
4. What columns do I want on companies? on contacts? on calls?
5. How do I want to score prospects?
6. What is the one signal that matters most?

Then build it in this order, showing me each piece before writing the next:
- docs/icp.md and docs/crm-rules.md based on my answers
- The 3 TSVs with the right headers (no rows)
- The Vite app: project setup first, then the parser and the read API, then the
  views, then the inline editing and audit
- Empty sidecar JSONs (crm-audit.json, crm-views.json)

Wait for my "yes" between steps.

The prompt is long on purpose. It teaches Claude the architecture I converged on, forces discovery before writing, and structures the build into steps you can validate one by one.

Which path to pick

Pick Path A if...Pick Path B if...
Your sales motion is similar to mine (B2B, calls, scoring, outbound + inbound).Your business is very different (e.g., self-serve SaaS, e-commerce, recruiting).
You want something running today.You have time to do real discovery and want full control.
You learn by tinkering with something that works.You learn by designing from a blank slate.

There is no wrong answer. Both paths land you at the same place: a CRM in a folder that Claude Code can read, write, and extend with one prompt.


After you have a CRM, write skills on top

Whichever path you took, the real unlock comes from skills — markdown files in .claude/skills/ that tell Claude how to handle recurring tasks.

I won't ship mine in the repo (they're tailored to my workflow). Here are three examples of skills you can adapt:

Example 1 — "update CRM" — triggers when you say "add this to the CRM". Detects whether the input is a call note, a CSV, or a free-form sentence. Scores against your ICP. Shows a proposed diff. Waits for your "yes".

Example 2 — "enrich CRM" — triggers on "fill the gaps". Identifies empty fields, tries to deduce them, then web-searches, then asks you about anything ambiguous.

Example 3 — "clean CRM" — triggers on "audit the CRM". Catches duplicates, broken links, typos in enums, bad date formats. Asks before fixing.

The pattern is always the same: read the brain files, read the current state, propose changes, wait for "yes", write. Your first skill should be the one task you do most. Mine was "log a call".


A few honest caveats

  • Built for solopreneurs or small teams. No "who edited what and when" with login, no permissions, no team collaboration mode yet. The single-user audit trail is there, but it's not multi-user.
  • You need to know Claude Code a bit. Nothing fancy, but it's a prerequisite.
  • This is opinionated. The structure assumes you do outbound + inbound, you score against an ICP, you track calls. If your business is very different, Path B is better than Path A.

What to do next

  1. Pick a path (A or B).
  2. Spend 30 minutes filling in icp.md and crm-rules.md. This is where the magic comes from.
  3. Use it for a week. Refine the rules whenever Claude scores wrong.
  4. Write your first skill. Mine was "log a call".

Questions or feedback: find me on LinkedIn.

Emma Guetta

About me

I'm Emma Guetta, co-founder of Retriever, a go-to-market AI agent that builds qualified prospect lists from a prompt (think Claude Code for go-to-market). Same principle as this guide: you describe what you want, an agent does the repetitive work, and you keep control of the result. Questions or feedback: find me on LinkedIn.