Remote OpenClaw Blog
How to Build a Personal CRM with OpenClaw (Gmail + Calendar + Vector Search)
9 min read ·
Remote OpenClaw Blog
9 min read ·
I have used HubSpot, Pipedrive, and Notion-based CRMs over the past four years. Every single one had the same problem: I stopped updating them after about three weeks. The friction of manually logging interactions, tagging contacts, and setting follow-up reminders was enough to kill the habit every time.
The insight that changed everything for me was realizing that my Gmail and Google Calendar already contain 90% of the relationship data I need. Every email exchange, every meeting, every calendar invite — it is all there, just not structured. What I needed was not another CRM interface to manually fill in. I needed an agent that could extract relationship data from the tools I already use and surface it when I need it.
That is exactly what OpenClaw can do. By combining three skills — Gmail scanning, Calendar integration, and a SQLite database with vector search — you can build a personal CRM that updates itself. No manual data entry. No tab-switching. No $50/month subscription. The inspiration for this approach came from similar setups I have seen discussed on Medium and in Google AI Studio community threads, but adapted specifically for OpenClaw's skill architecture.
The personal CRM has four layers, each handled by a separate OpenClaw skill or configuration:
| Layer | Tool | Purpose |
|---|---|---|
| Data Ingestion (Email) | Gmail Skill | Scans inbox for new interactions, extracts sender/recipient, subject, date, and key content |
| Data Ingestion (Meetings) | Calendar Skill | Pulls meeting attendees, titles, and notes from Google Calendar events |
| Storage | SQLite + Vector DB | Stores contacts, interaction history, and semantic embeddings for search |
| Action | Follow-Up Skill | Checks last-contact dates and sends reminders via Telegram or email |
The data flows in one direction: Gmail and Calendar feed into the SQLite database, the database gets enriched with vector embeddings, and the follow-up skill queries the database to decide who needs attention. OpenClaw orchestrates the entire pipeline on a schedule — I run mine every morning at 7 AM.
The Gmail skill is the primary data source. It connects to your Gmail account via OAuth, scans for new messages since the last run, and extracts structured contact data from each conversation.
Here is the skill configuration I use:
# skills/crm-gmail-scan.yaml
name: crm-gmail-scan
description: Scan Gmail for new interactions and extract contact data
schedule: "0 7 * * *" # Daily at 7 AM
triggers:
- cron
gmail:
scopes:
- gmail.readonly
query: "newer_than:1d -category:promotions -category:social"
max_results: 50
actions:
- extract_contacts:
fields:
- name
- email
- company (inferred from domain)
- interaction_type (sent/received)
- subject
- date
- summary (LLM-generated 1-line summary)
- upsert_to_sqlite:
database: ~/openclaw/crm.db
table: contacts
conflict_key: email
The key detail here is the query filter. By excluding promotions and social categories, you avoid polluting your CRM with marketing emails and social media notifications. The newer_than:1d filter ensures you only process new messages on each run, keeping the processing fast.
The LLM-generated summary field is where OpenClaw adds real value. Instead of storing raw email content (which is bulky and hard to search), the agent generates a one-line summary of each interaction: "Discussed Q2 pricing proposal for cloud migration project" or "Confirmed lunch meeting for Thursday." These summaries become the basis for vector search later. For more detail on Gmail integration, see the OpenClaw Gmail integration guide.
Gmail captures written communication, but meetings are where the most important relationship-building happens. The Calendar skill pulls attendee data and meeting context from Google Calendar.
# skills/crm-calendar-sync.yaml
name: crm-calendar-sync
description: Sync Calendar meetings to CRM contacts
schedule: "0 7 * * *"
triggers:
- cron
google_calendar:
scopes:
- calendar.readonly
time_range: "past_24h"
ignore_all_day: true
actions:
- extract_attendees:
fields:
- name
- email
- meeting_title
- meeting_date
- duration_minutes
- notes (from event description)
- upsert_to_sqlite:
database: ~/openclaw/crm.db
table: contacts
conflict_key: email
- log_interaction:
database: ~/openclaw/crm.db
table: interactions
type: meeting
I found that calendar data is often more valuable than email data for relationship tracking. An email might be a quick acknowledgment, but a 30-minute meeting represents real investment from both parties. My follow-up logic weights meeting interactions higher than email when calculating relationship strength. For the full Calendar skill setup, see the Google Calendar integration guide.
SQLite is the right choice for a personal CRM database for three reasons: it is serverless (just a file on disk), it handles concurrent reads well, and it is small enough to back up by copying a single file. Here is the schema I use:
-- crm-schema.sql
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
name TEXT,
company TEXT,
first_seen DATE,
last_contact DATE,
interaction_count INTEGER DEFAULT 0,
relationship_score REAL DEFAULT 0.0,
tags TEXT, -- JSON array
notes TEXT,
embedding BLOB -- vector embedding for semantic search
);
CREATE TABLE IF NOT EXISTS interactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_email TEXT NOT NULL,
type TEXT NOT NULL, -- email_sent, email_received, meeting
subject TEXT,
summary TEXT,
date DATETIME NOT NULL,
FOREIGN KEY (contact_email) REFERENCES contacts(email)
);
CREATE INDEX idx_contacts_last_contact ON contacts(last_contact);
CREATE INDEX idx_interactions_date ON interactions(date);
The relationship_score field is calculated by OpenClaw based on interaction frequency, recency, and type. A contact you met with last week scores higher than someone who emailed you once three months ago. This score drives the follow-up priority system.
This is where the CRM goes from "useful" to "genuinely better than a paid tool." Vector embeddings let you search your contacts by meaning, not just keywords.
When OpenClaw processes a new contact or interaction, it generates a text embedding of the combined notes, summaries, and tags. These embeddings are stored directly in the SQLite database as BLOBs. For search, OpenClaw converts your query into an embedding and finds the closest matches using cosine similarity.
Marketplace
Free skills and AI personas for OpenClaw — browse the marketplace.
Browse the Marketplace →# skills/crm-vector-search.yaml
name: crm-vector-search
description: Semantic search across CRM contacts
triggers:
- command: "/crm search {query}"
embedding:
model: nomic-embed-text # via Ollama, runs locally
dimensions: 768
actions:
- embed_query:
input: "{query}"
- search_sqlite:
database: ~/openclaw/crm.db
table: contacts
column: embedding
similarity: cosine
top_k: 10
- format_results:
template: |
**{name}** ({company})
Last contact: {last_contact}
Score: {relationship_score}
Recent: {latest_interaction_summary}
In practice, this means I can type /crm search "someone working on AI infrastructure in Europe" and get back relevant contacts even if none of them have those exact words in their records. The embedding model understands that "ML ops engineer at a Berlin startup" is semantically close to "AI infrastructure in Europe." I use nomic-embed-text through Ollama, which runs locally and costs nothing.
The follow-up system is the highest-value component. It queries the database for contacts whose last_contact date exceeds a configurable threshold, sorted by relationship_score, and sends you a daily digest.
# skills/crm-follow-ups.yaml
name: crm-follow-ups
description: Daily follow-up reminders for stale contacts
schedule: "0 8 * * 1-5" # Weekdays at 8 AM
triggers:
- cron
rules:
- tier: high_value
min_score: 7.0
stale_after_days: 14
max_reminders: 5
- tier: medium_value
min_score: 4.0
stale_after_days: 30
max_reminders: 3
- tier: low_value
min_score: 1.0
stale_after_days: 60
max_reminders: 2
actions:
- query_stale_contacts:
database: ~/openclaw/crm.db
- generate_digest:
format: markdown
include:
- contact name and company
- days since last contact
- last interaction summary
- suggested follow-up action (LLM-generated)
- send_telegram:
chat_id: your-chat-id
message: "{digest}"
The tiered system prevents alert fatigue. High-value contacts (score above 7.0) trigger a reminder after just two weeks without contact. Medium-value contacts get 30 days. Low-value contacts get 60. The LLM-generated "suggested follow-up action" is surprisingly useful — it reads the last interaction summary and suggests something specific: "Follow up on the proposal you discussed on March 15" rather than a generic "You haven't talked to John in a while."
Once all four skills are configured, the daily workflow looks like this:
I have been running this exact setup for about six weeks now. My follow-up rate has gone from "I forget half the time" to "I follow up within the threshold for 90%+ of important contacts." The vector search has been genuinely useful about twice a week — usually when someone mentions a topic in a meeting and I need to recall who else I know working in that space.
The entire system runs on my Mac Mini alongside other OpenClaw skills. CPU usage during the daily run is negligible — under 5% for about 90 seconds. The SQLite database is currently at 12MB with about 800 contacts.
This CRM is not for everyone, and I want to be transparent about where it falls short:
For what it is — a free, self-hosted, privacy-respecting personal CRM that updates itself — I think it is one of the most practical things you can build with OpenClaw. The OpenClaw Marketplace has pre-built skills that can shortcut some of this setup.
For solo operators and small teams (under 10 people), yes. OpenClaw's personal CRM handles contact storage, interaction history, follow-up reminders, and search — which covers 80% of what most individuals actually use in a paid CRM. What you lose is the polished UI, team collaboration features, and pre-built reporting dashboards. If you primarily need a system that remembers who you talked to and reminds you to follow up, OpenClaw does that for free.
Regular search matches exact keywords — if you search "cloud infrastructure" you only find contacts where those exact words appear. Vector search converts contact notes into mathematical embeddings, so searching "cloud infrastructure" also surfaces contacts tagged with "AWS migration," "server deployment," or "DevOps consulting" because the meanings are similar. This semantic matching is dramatically more useful when your contact database grows beyond a few hundred people.
The guide focuses on Gmail because OpenClaw's Gmail skill is the most mature email integration. However, you can adapt the approach for Outlook by using the Microsoft Graph API skill or an IMAP-based skill. The SQLite database and vector search layers are email-provider agnostic — only the ingestion skill changes.
Very little. A SQLite database with 5,000 contacts, full interaction history, and vector embeddings typically uses under 500MB of disk space. SQLite is extremely efficient for single-user databases. You could run this on a Mac Mini, a VPS with 10GB of storage, or even a Raspberry Pi without storage concerns.