Nick Jungmarker · Case study · production system
An AI CRM for my real estate team
Leads from my website, contracts with eight deadlines each, and follow-ups across a two-agent team were slipping through spreadsheets and email, and a missed contract date can cost a client money. I wanted a CRM that also reads: one where Claude reads contracts and drafts follow-ups, and a person approves anything that changes a record or reaches a client.
- Who uses it
- Me and the second agent on my team. Every record is scoped to the agent who owns it.
- Status
- In use. Built from February 2026; the code is private because it holds client data.
- My role
- Designed the rules and data model, built it with Claude Code as a pair programmer, and run it day to day.
A contract, end to end
1. System
Intake
A ratified contract arrives by upload or the deal inbox. A lead arrives from a website form.
2. Claude
Extract or draft
Claude pulls the parties, price and eight deadlines with supporting quotes, or tags a lead’s areas and language.
3. Agent
Review
The deal waits in “Needs review”. Each date read by the AI is marked “Not verified”, and a missing one is “Unknown”, never “Waived”.
4. Agent
Confirm
The agent confirms. The change runs through the same functions as the dashboard and is written to the audit log.
5. Claude
Follow up
Reminders track each confirmed milestone. The daily agent drafts check-ins that wait for the agent to send them.
Leads follow the same pattern: Claude tags a new lead’s areas and language. If the call fails, times out or the key is missing, a keyword matcher takes over, so the form never waits more than about 8 seconds, and the lead lands with the right agent and email series. There are no screenshots here because the live system holds client data.
What I can show
Safeguards Implementation fact
- AI writes are proposals. Nothing changes until the record’s owner confirms.
- No AI tool sends email or texts, or deletes records.
- Every record belongs to one agent, and a record you don’t own looks the same as one that doesn’t exist.
- Text from emails, notes and alerts is treated as untrusted input.
- Every AI action is logged with its actor and tool, and rate-limited.
Each one is enforced in code; details are in the eight rules below.
Limitations
- I haven’t measured time saved, response times or conversion, so this page describes behavior, not outcomes.
- It serves one two-agent team. Search is Postgres full-text rather than embeddings, which is enough for one book of business and may not be for more.
- Voice notes use the browser’s speech recognition, which does worse on long recordings than server transcription.
- Contract extraction is only as good as the review: the safeguard is that a person confirms every date, not that the AI is always right.
Problems I found and fixed
Found: A contingency left blank could be read as waived, so a missing date and a waived one looked the same.
Changed: Each deadline now has a state: Unknown, Not verified, Confirmed, Waived, Not applicable or Completed. Dates read by the AI stay Not verified and are flagged until the agent confirms them. Reminders skip dates that are Waived, Not applicable or Unknown.
Found: A two-agent review found the daily follow-up run, started from the team view, filed the second agent’s follow-ups under me and blocked hers for the week.
Changed: The team-view run now covers only my records, reassigning a contact moves its tasks and suggestions with it, and regression tests cover both.
Found: A security pass found the scheduled-job endpoints trusted a request header that can be forged.
Changed: They now require a secret and fail closed. Sign-in links trust only the site’s own hosts.
Decisions and tradeoffs
| Decision | Why | When I’d revisit |
|---|---|---|
| Postgres full-text search, not vector embeddings | No new service or cost, and it’s enough for one team’s book of business | If recall on memory questions drops; pgvector is the upgrade path |
| Pure logic files with no imports | Routing, deadlines, matching and the work queue are tested directly with node --test, no database needed | Not planning to; it keeps tests fast |
| MCP over plain JSON responses, no streaming | Works with serverless functions and every client I use | If a tool needs long-running progress updates |
| Browser speech recognition for voice notes | Free and runs on the device | For long recordings, where server transcription does better |
| Haiku for tagging and drafts, a larger model for the copilot | Tagging must be fast and cheap; the copilot needs reasoning over several tool calls | Re-check as models and prices change |
Postgres full-text search, not vector embeddings
Why: No new service or cost, and it’s enough for one team’s book of business
Revisit: If recall on memory questions drops; pgvector is the upgrade path
Pure logic files with no imports
Why: Routing, deadlines, matching and the work queue are tested directly with node --test, no database needed
Revisit: Not planning to; it keeps tests fast
MCP over plain JSON responses, no streaming
Why: Works with serverless functions and every client I use
Revisit: If a tool needs long-running progress updates
Browser speech recognition for voice notes
Why: Free and runs on the device
Revisit: For long recordings, where server transcription does better
Haiku for tagging and drafts, a larger model for the copilot
Why: Tagging must be fast and cheap; the copilot needs reasoning over several tool calls
Revisit: Re-check as models and prices change
In October 2026 I also migrated the segment email drips (buyers, sellers, investors, first-time buyers, past clients) from ActiveCampaign to REV (BoldTrail).
How it fits together
Leads come from the website, documents come through a deal inbox, and I work the CRM through a dashboard copilot or any MCP client such as Claude Desktop. All three paths share the same tools, the same ownership checks and the same audit log.
- Website forms → AI tagging with a keyword fallback → contact and email series
- Deal inbox and uploads → AI extraction → “Needs review”
- Copilot and MCP clients → reads run, writes become proposals
- A person confirms → same functions as the dashboard → one audit log
The eight rules in detail
Rule 1: Reads run. Writes are proposals.
When the copilot wants to add a task, update a stage or draft an email, it creates a proposal. Nothing changes until the agent who owns the record clicks Confirm. Proposals call the same functions as the dashboard, so validation is identical.
Rule 2: The AI can’t send, delete or quietly edit.
No AI tool sends email or texts, and none deletes records. The MCP server is read-only except for one tool that queues a note for review.
Rule 3: “Not yours” looks the same as “doesn’t exist.”
Every record belongs to one agent. Queries are scoped automatically, and a record you don’t own returns the same 404 as a missing one, so IDs never reveal what exists. A test scans every API file and fails on any unscoped query of an owned table.
Rule 4: Outside text is untrusted.
Notes, emails and alerts can contain instructions, and email senders can be forged. MLS alert emails may only ever touch listings, never leads or deals, and every change they make is logged. The MCP server tells clients not to follow instructions found in CRM data.
Rule 5: A slow AI call doesn’t hold up a lead.
When a lead submits a form, Claude tags the areas and language. If the call fails, times out or the key is missing, a keyword matcher takes over, so the form waits at most about 8 seconds and the lead is still created and routed.
Rule 6: A person checks every extracted date.
Claude reads the ratified contract and pulls out the parties, price and eight deadlines with supporting quotes. The deal stays in “Needs review” until the agent confirms every date. The PDF isn’t stored by the reader.
Rule 7: Counts come from the rows they describe.
The work queue’s counts are computed from the same items the filter shows, so clicking “3 overdue” shows those three rows. If a count’s inputs fail to load, it shows “–” rather than 0.
Rule 8: Every AI action is logged and limited.
Copilot, MCP and inbox actions all write to one log with the actor and tool. Rate limits cap tool calls and captures per hour.
What it does
Lead intake and routing
Forms and a down-payment program quiz create the contact in REV, tag it (buyer MD or DC, seller, CMA, investor, first-time, Spanish) and start the matching drip. The second agent’s leads go only to her.
routing, fallback, bilingual
Copilot and MCP server
Ask “who haven’t I talked to in 90 days?” from the dashboard, Claude Code or Claude Desktop. Tools search contacts, memories, tasks, pipeline and deal deadlines.
MCP, tool use, proposals
Transaction coordinator
Contracts arrive by upload or a deal inbox. Claude extracts the dates, the agent confirms them, and a reminder engine tracks each milestone, including whether a date is trusted yet.
extraction, deadlines, review
Relationship memory
Facts a client mentions are stored with their source, confidence and whether I verified them. Newer verified facts replace older ones. Answers to questions cite where each fact came from.
citations, full-text search
Daily follow-up agent
Each morning it finds overdue and stale contacts, likely duplicates and missing info, and drafts check-ins for the top ten. Drafts wait for me. Nothing is sent.
signals, drafts, dedupe
Traffic and AI search
Google and Bing search queries, on-site actions and referrals from AI assistants. Sellers get a private link to their listing’s online activity. Pages are prerendered with structured data so AI assistants can read them.
analytics, SEO and AEO, llms.txt
Stack
- App
- Vite, React, TypeScript
- API
- Serverless TypeScript functions
- Data
- Postgres (Supabase), private file storage
- AI
- Claude via the Anthropic SDK; Haiku for quick tagging and drafts
- Email drips
- REV / BoldTrail, migrated off ActiveCampaign in Oct 2026
- Other
- Search Console and Bing data, first-party analytics, Obsidian sync
For scale: 178 commits from February to November 2026, and 7 tools on the MCP server, all read-only except one that queues a note for review.
From a two-agent team to a sales org
- Ledgerly CRM MCP
Rules 1 to 4 and 8 at company scale: roles and territories, approval by someone other than the requester, rollback, tamper-evident audit evidence.
- Lead-to-Pipeline Systems
My lead routing, redesigned as Salesforce lifecycle stages, routing, validation rules, a test plan and release notes.
- GTM Signal Agent
The follow-up agent for account teams, with an evaluation suite that scores every change.
- Weekly Business Review Pack
Rule 7 as an executive pack: every headline number checked against a second source.
- Churn Early Warning
Who’s going quiet, back-tested, with plain-English reasons an agent can quote.
- Campaign Lift Lab
What actually causes a booked call, measured with holdouts instead of dashboards.
How it was built: Nick Jungmarker with Claude Code as a pair programmer. I wrote the rules and failure cases, reviewed the changes and keep the tests green; Claude wrote most of the code. This page describes the system without showing code or client information.